1.init过程运行过程
init过程是有内核启动的第一个用户级过程。
2.init过程源码剖析
1.次要性能
- 子过程的终止解决
- 生成设施节点
- 提供属性服务,保留运行所需的环境变量
- 剖析init.rc启动脚本文件
2.根本流程
(1)注册与子过程相干的SIGCHLD信号处理器,这里只是用作告诉,具体的工夫解决在init的事件处理循环中。
(2)创立并挂载启动所需的文件目录
<code class="c">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
<code class="c">action_for_each_trigger("early-init", action_add_queue_tail);//将参数中的命令保留到运行队列中 drain_action_queue();//将运行队列中的命令逐个取出执行。
(7)创立init过程中曾经定义好的设施节点文件。
device_fd = device_init();
(8)初始化属性服务
<code class="c">property_init();
(9)将Android启动Logo显示再LCD屏幕上。
<code class="c">#define INIT_IMAGE_FILE "/initlogo.rle" load_565rle_image(INIT_IMAGE_FILE);
(10)设置属性: property_set()函数。
(11)执行动作列表中 init 片段中的命令。
<code class="c">action_for_each_trigger("init", action_add_queue_tail); drain_action_queue();
(12)启动属性服务
(13)创立套接字 ==接管==子过程终止的SIGCHLD信号,调用相干handler过程解决。
(14)执行动作列表中 early-boot, boot, property相干的命令。
(15)设置事件处理的监听事件
3.init.rc脚本文件剖析与执行
两个性能
- 设置零碎环境,记录待执行的过程
- action_list 与 service_list相干的内容。
3.1 动作列表
(1) on init
次要设置环境变量,生成零碎运行所需的文件或目录
(2)on boot
次要设置利用终止条件
(3)on property : <name> = <value>
记录属性扭转时执行的命令
3.2 服务列表
sevice 段落用来记录init过程启动的过程。
由init过程启动的子过程或是一次性程序,或是运行在后盾的与应用程序、零碎相干的守护过程。
service段落中的服务全副注册在服务列表中,init过程从该列表中顺次取出相应服务,并启动它。
3.3init.rc文件剖析函数
parse_config_file() 用来剖析init.rc脚本文件。
<code class="c">int parse_config_file(const char *fn) { char *data; data = read_file(fn, 0); //读取文件到内存中,保留为字符串格局,返回字符串在内存中的初始地址 parse_config(fn, data); //剖析read_file函数返回的字符串,并生成动作列表和参数列表。 }
(1)parse_config()函数
<code class="c">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)动作列表的运行
1)获取动作列表的 head
2)从action列表取出动作列表 转换成command构造体
3)执行 动作列表中动作对应的函数。
(2)服务列表的运行
通过on boot段落中的最初一行命令 class_start 运行service 段落中所有的程序。
service_start 通过执行execve()零碎调用来运行服务列表中的程序。
4.创立设施节点文件。
设施节点文件是设施驱动的逻辑文件
- 冷插拔:以事后定义的设施信息为根底,当init过程被启动运行时,对立创立设施节点文件
- 热插拔:零碎运行时,为插入的设施动态创建设施节点文件。
Linux中 udev守护过程创立设施节点文件
4.1创立动态设施节点
冷插拔机制
在init过程启动后,在/sys下读取实现注册好的设施信息,而后引发与各设施绝对应的uevent,创立设施节点文件。
举例Binder驱动程序
(1)binder驱动程序在初始化函数中调用misc_rigister()函数,将创立设施节点的所需的信息保留在/sys目录下。
(2)相干的节点文件是曾经保留在/system/core/init/device.c中,devperms构造体。
这里的这个文件充当一个列表,
(3)init过程调用device_init()函数
<code class="c">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()函数,创立设施节点文件。
5.过程的终止与再启动。
过程再启动代码剖析
子过程终止时,init过程接管传递过去的SIGCHLD信号,调用与之对应的处理函数sigchld_handler(),
<code class="c">static void sigchld_handler(int s) { write(signal_fd, &s, 1); }
signal_fd记录信号编号,调用wait_for_one_process()函数被调用。
- 产生SIGCHLD信号时,程序从监听状态中跳出,执行poll()函数。
- wait_for_one_process()函数在产生SIGCHLD信号的过程的服务列表中,查看过程的设置选项
wait_for_one_process()函数体:
<code class="c">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()函数就会被调用执行。
<code class="c">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查看权限后,再批改属性指。
<img src=”https://gitee.com/zhang_menglin965/blogimage/raw/master/img/202112011448356.png” alt=”image-20211201144822301″ style=”zoom:67%;” />
6.1属性初始化
<code class="c">void property_init(void) { init_property_area();//属性域初始化。 load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); } int main (int argc, char **argv) { ... property_init();//在共享内存中生成属性域 ... }
<code class="c">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()函数。
<code class="c">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);//更改属性值, } ... } ... } }