IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android init学习 -> 正文阅读

[移动开发]Android init学习

1.init进程运行过程

init进程是有内核启动的第一个用户级进程。

2.init进程源码分析

1.主要功能

  • 子进程的终止处理
  • 生成设备节点
  • 提供属性服务,保存运行所需的环境变量
  • 分析init.rc启动脚本文件

image-20211130101746282

2.基本流程

(1)注册与子进程相关的SIGCHLD信号处理器,这里只是用作通知,具体的时间处理在init的事件处理循环中。

(2)创建并挂载启动所需的文件目录

open_devnull_stdio();//创建日志输出设备
log_init();//生成/dev/__kmsg__设备节点文件,输出log信息。

(3)解析init.rc文件
init.rc和 init.{hardware}.rc两个文件
生成服务列表和动作列表。 分别以链表的形式注册到service_list和action_list中。

(4)初始化QEMU 设备,

(5)解析init.{hardware}.rc文件

(6)执行early-init,片段中的命令
init, early-boot, boot

action_for_each_trigger("early-init", action_add_queue_tail);//将参数中的命令保存到运行队列中
drain_action_queue();//将运行队列中的命令逐一取出执行。

(7)创建init进程中已经定义好的设备节点文件。

device_fd = device_init();

(8)初始化属性服务

property_init();

(9)将Android启动Logo显示再LCD屏幕上。

#define INIT_IMAGE_FILE "/initlogo.rle"
load_565rle_image(INIT_IMAGE_FILE);

(10)设置属性: property_set()函数。

(11)执行动作列表中 init 片段中的命令。

action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();

(12)启动属性服务
(13)创建套接字 接收子进程终止的SIGCHLD信号,调用相关handler进程处理。

(14)执行动作列表中 early-boot, boot, property相关的命令。

image-20211130105140551

(15)设置事件处理的监听事件

image-20211130111142878

3.init.rc脚本文件分析与执行

两个功能

  • 设置系统环境,记录待执行的进程
  • action_list 与 service_list相关的内容。

3.1 动作列表

(1) on init

主要设置环境变量,生成系统运行所需的文件或目录

image-20211130112602356

(2)on boot

主要设置应用终止条件

image-20211130112716901

(3)on property : =

记录属性改变时执行的命令

image-20211130112848629

3.2 服务列表

sevice 段落用来记录init进程启动的进程。

由init进程启动的子进程或是一次性程序,或是运行在后台的与应用程序、系统相关的守护进程。

service段落中的服务全部注册在服务列表中,init进程从该列表中依次取出相应服务,并启动它。

3.3init.rc文件分析函数

parse_config_file() 用来分析init.rc脚本文件。

int parse_config_file(const char *fn)
{
	char *data;
	data = read_file(fn, 0);  //读取文件到内存中,保存为字符串格式,返回字符串在内存中的初始地址
	parse_config(fn, data);   //分析read_file函数返回的字符串,并生成动作列表和参数列表。
}

(1)parse_config()函数

static void parse_config(const char *fn, char *s)
{
	for(;;){
		switch (next_token(&state)){   //以行为单位分割参数传递过来的字符串。
			case T_NEWLINE:
			if(nargs) {
				int kw = lookup_keyword(args[0]);//返回每行首个单词在keyword_list结构体数组中的数组编号。
				if (kw_is(kw, SECTION)){ //SECTION分组区分动作列表和服务列表
					parse_new_section(&state, kw, nargs, args);//将筛选出的命令注册动作列表或者服务列表中。
				}
			}
		}
	}
}

3.4 动作列表与服务列表的运行

(1)动作列表的运行

image-20211201102200476

1)获取动作列表的 head

2)从action列表取出动作列表 转换成command结构体

3)执行 动作列表中动作对应的函数。

(2)服务列表的运行

image-20211201102558119

通过on boot段落中的最后一行命令 class_start 运行service 段落中所有的程序。

service_start 通过执行execve()系统调用来运行服务列表中的程序。

4.创建设备节点文件。

设备节点文件是设备驱动的逻辑文件

  • 冷插拔:以预先定义的设备信息为基础,当init进程被启动运行时,统一创建设备节点文件

  • 热插拔:系统运行时,为插入的设备动态创建设备节点文件。

Linux中 udev守护进程创建设备节点文件

image-20211201104315705

4.1创建静态设备节点

冷插拔机制
在init进程启动后,在/sys下读取实现注册好的设备信息,而后引发与各设备相对应的uevent,创建设备节点文件。

举例Binder驱动程序

(1)binder驱动程序在初始化函数中调用misc_rigister()函数,将创建设备节点的所需的信息保存在/sys目录下。

(2)相关的节点文件是已经保存在/system/core/init/device.c中,devperms结构体。
这里的这个文件充当一个列表,
(3)init进程调用device_init()函数

ini device_init(void)
{
	fd = open_uevent_socket();//创建一个套接字,接收uevent
	to = get_usecs();
	coldboot(fd, "/sys/class");//调用 do_coldboot()函数。
	coldboot(fd, "/sys/block");
	coldboot(fd, "/sys/devices");
	t1 = get_usecs();
	log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
}

static void do_coldboot(int event_fd, DIR *d)
{
    fd = openat(dfd, "uevent", O_WRONLY);
    if(fd >= 0){
        write(fd, "add\n", 4);//写入"add"信息,强制引起uevent
        close(fd);
        handle_device_fd(event_fd);//handler_device_fd()函数接收相关的uevent.
    }
}

void handle_device_fd(int fd)
{
    while((n = recv(fd, msg, UEVENT_MSG_LEN)) > 0){
        struct uevent uevent;
        parse_event(msg, &uevent);//将uevent信息写入uevent结构体中
        handle_devive_event(&uevent);//创建节点文件
    }
}

static void handle_device_event(strut uevent *uevent)
{
    
    if(!strncmp(uevent->subsystem, "block", 5)){
        block = 1;
        base = "/dev/block/";
        mkdir(base, 0755);
    }
    .
    .
    else
		base = "dev";
    if(!strcmp (uevent->action, "add)){
     	make_device(devpath, block, uevent->major, uevent->minor);//调用mknod()函数,创建设备节点文件。           
     }
}

总结:驱动程序将创建设备节点所需的信息保存到/sys目录下,
相关的节点文件 文件已经保存在 /system/core/init/device.c中
init进程启动后调用 docoolboot(),写入add 强制引起uevent
从列表中获取相关的节点文件,创建设备节点文件。

uevent.

4.2动态设备感知

热插拔由init进程的事件处理循环来完成。
调用handle_device_fd()函数,创建设备节点文件。

image-20211201131456219

5.进程的终止与再启动。

image-20211201132210115

进程再启动代码分析

子进程终止时,init进程接收传递过来的SIGCHLD信号,调用与之对应的处理函数sigchld_handler(),

static void sigchld_handler(int s)
{
	write(signal_fd, &s, 1);
}

image-20211201140046180

signal_fd记录信号编号,调用wait_for_one_process()函数被调用。

  • 发生SIGCHLD信号时,程序从监听状态中跳出,执行poll()函数。
  • wait_for_one_process()函数在产生SIGCHLD信号的进程的服务列表中,检查进程的设置选项

wait_for_one_process()函数体:

static int wait_for_one_process(int block)
{
	...
	//回收进程所占用的资源。
    while( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1
												&& errno == EINTR );
    //用来取出与服务列表中终止进程相关的服务项目。 
    svc = service_find_by_pid(pid);
    //在取出的服务项目选项中,检查SVC_ONESHOT是否被设置。  如果已经设置了,直接终止。
    if(!(svc->flags & SVC_ONESHOT)){
    	kill(-pid, SIGKILL);
    }
    /* remove any socket we may have created*/
    for(si = svc->sockets; si; si = si->next){
	//删除所有进程持有的socketDescriptor
        unlink(tmp);
    }
    svc->pid = 0;
    svc->flags &= (~SVC_RUNNING);//取消正在运行标记。
    
    if(svc->flags & SVC_ONESHOT) {//设置进程标记为SVC_DISABLED,从wait_for_one_process中跳出。
    	svc->flags |= SVC_DISABLED;
    }
    if(svc->flags & SVC_DISABLED)
		return 0;
    list_for_each(node, &svc->onrestart.commands) {//重新启动相关进程????
        cmd = node_to_item(node, struct command, clist);
        cmd->func(cmd->nargs, cmd->args);
    }
   	svc->flags |= SVC_RESTARING;
}

wait_for_one_process()函数执行完毕后,事件处理循环中的restart_processes()函数就会被调用执行。

static void rstart_service_if_needed(struct service *svc)
{
	svc->flags &= (~SVC_RESTSRING);
	service_start(svc);
	return;
}
static void restart_process()
{
	process_needs_restart = 0;
	service_for_each_flags(SVC_RESARTING, restart_service_if_needed);
}
//运行服务列表中带有SVC_RESTART标记的进程。当一个带有此标记的进程被终止,产生SIGCHLD信号时,restart_process()函数将重新启动它。

6.属性服务

只有init进程才能修改属性指,其他进程修改属性值时,必须向init进程提出请求,init检查权限后,再修改属性指。

image-20211201144822301

6.1属性初始化

void property_init(void)
{
    init_property_area();//属性域初始化。
	load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
int main (int argc, char **argv)
{
    ...
    property_init();//在共享内存中生成属性域
    ...
}
int start_property_service(void)
{
	int fd;
    //读取存储在各文件中的基本设置,将他们设置为属性指。
	load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
	load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
	load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);	
	//read persistent properties after all default values have been
    //读取系统运行时其他进程新生成的属性值或者更改的属性值。
	load_persistent_properties();
    //创建/dev/socket/property_service的Unix域套接字
	fd_createsocket(PROP_SERVICE NAME, SOCK STREAM, 0666, 0, 0);
	if(fd < 0) return -1;
	fcntl(fd, F_SETFD, FD CLOEXEC);
	fcntl(fd, F_SETFL, 0 NONBLOCK)

    listen(fd, 8);
	return fd
}
int main (int argc, char **argv)
{
	property_set_fd = start_property_service();//start_property_service()函数,创建启动属性服务所需要的Unix域套接字。
}

6.2属性变更请求处理

接收到属性变更请求后,init进程就会调用handle_property_set_fd()函数。

void handle_property_set_fd(int fd)
{
	...
	/* check socket options here */
	if(getsockopt(s, SOL_SOCKER, SO_PEERCRED, &cr, &cr_size) < 0){
        //获取SO_PEERCRED值, 以便检查传递信息进程的访问权限
		close(s);
		ERROR("Unable to recieve socket options\n");
		return;
    }
    ...
    switch(msg.cmd) {
        case PROP_MSG_SETPROP:
            ...
            if(memcmp(msg.name, "ctl." , 4) ==0 ){//ctl消息是请求进程启动与终止的消息。
                if(check_control_perms(msg.value, cr.uid)){//检查访问权限
                    handle_control_message((char *)msg.name + 4, (char*) msg.value);
                }
                ...
            }else{
                if(check_perms(msg.name, cr.uid)){//检查访问权限
                    property_set((char*)msg.name, (char*)msg.value);//更改属性值,
                }
                ...
            }
            ...
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 16:52:58  更:2021-12-02 16:54:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:56:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码