Android 4.4 Init进程分析五 :进程的终止与再启动
****************************************************************************
Android 4.4 init进程分析文章链接
Android 4.4 Init进程分析一 :Android init进程概述
Android 4.4 Init进程分析二 :Android初始化语言
Android 4.4 Init进程分析三:init.rc脚本文件的解析
Android 4.4 Init进程分析四 :init.rc脚本文件的执行
Android 4.4 Init进程分析五 :进程的终止与再启动
***************************************************************************
1 概述
init进程读取并分析init.rc文件,获得服务列表service_list,而后从列表中依次启动服务子进程。
init进程启动的主要进程如下:
- sh: 搭载Android的机器终端,连接串口或adbd时,提供控制台输入输出的shell程序。
- adbd: 指Android Debug Bridge,原来管理QEMU模拟器或实际机器的状态。该工具运行在目标机器(模拟器或物理机器)上,充当服务器,PC充当连接服务器的客户端。
- servicemanager: 是Android比较正要的一个进程,在init进程启动后启动,用来管理系统中的服务。
- vold: 指Volume Dameon,用来挂载/管理USB存储或SD卡设备。
除了以上这些进程外,init进程还启动其他多种进程。若init启动的某个进程终止,则会对系统的运行产生影响。比如“服务管理器”(servicemanager),它是应用程序使用系统服务必须运行的进程。如果该进程出现意外终止,那么进程间的通信、图像输出、音频输出等功能都将无法使用。因此在init启动的进程中,除了一小部分外,其它大部分进程出现意外终止时,init进程都要重新启动它们。
通过前面两篇文章的分析,我们已经知道init进程会启动服务列表中的服务,创建相应的子进程。当子进程意外终止时,会向父进程init进程传递SIGCHLD信号,init进程接收到该信号后,检查进程选项是否设置为oneshot,若设置为oneshot,init进程放弃重启进程;否则重启进程。
关于init进程如何重启子进程,接下来详细分析
2 进程再启动代码分析
关于进程(service)的重启,我们还要从init进程的main()函数说起,在main()函数中有一个“内建action”: signal_init
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#1065
1 queue_builtin_action(signal_init_action, "signal_init");
当init进程执行这个action时就回去调用signal_init_action()函数:
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#800
1 static int signal_init_action(int nargs, char **args) 2 { 3 signal_init(); 4 return 0; 5 }
进而调用到signal_init()函数:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#131
1 void signal_init(void) 2 { 3 int s[2]; 4 5 struct sigaction act; 6 memset(&act, 0, sizeof(act)); 7 act.sa_handler = sigchld_handler; 8 act.sa_flags = SA_NOCLDSTOP; 9 sigaction(SIGCHLD, &act, 0); 10 11 /* create a signalling mechanism for the sigchld handler */ 12 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { 13 signal_fd = s[0]; 14 signal_recv_fd = s[1]; 15 fcntl(s[0], F_SETFD, FD_CLOEXEC); 16 fcntl(s[0], F_SETFL, O_NONBLOCK); 17 fcntl(s[1], F_SETFD, FD_CLOEXEC); 18 fcntl(s[1], F_SETFL, O_NONBLOCK); 19 } 20 21 handle_signal(); 22 }
signal_init()中调用了sigaction()函数,注册了处理SIGCHLD信号的信号处理函数。也就是当init的子进程终止时,会向父进程init进程传递SIGCHLD信号,init进程接收到该信号后调用信号处理函数sigchld_handler()函数来处理。
同时该函数还创建了一个 sockectpair:signal_fd(发送) and signal_recv_fd(接收)
sigchld_handler()函数定义如下:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#36
1 static void sigchld_handler(int s) 2 { 3 write(signal_fd, &s, 1); 4 }
这个函数作用就是进行socket通信,把信息传递给接收端,即:
向signal_init()中创建的“socket pair”里的signal_fd写数据,于是“socket pair”的另一个句柄signal_recv_fd就可以得到所写的数据。
signal_recv_fd接收数据及处理又是在哪里呢?答案是init进程的mian()函数中:
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#1096
1 int main(int argc, char **argv) 2 { 3 . . . . . . 4 . . . . . . 5 for(;;) { 6 . . . . . . 7 if (!signal_fd_init && get_signal_fd() > 0) { 8 ufds[fd_count].fd = get_signal_fd(); // 就是signal_recv_fd ! 9 ufds[fd_count].events = POLLIN; 10 ufds[fd_count].revents = 0; 11 fd_count++; 12 signal_fd_init = 1; 13 } 14 . . . . . . 15 . . . . . . 16 nr = poll(ufds, fd_count, timeout); 17 . . . . . . 18 for (i = 0; i < fd_count; i++) { 19 if (ufds[i].revents == POLLIN) { 20 if (ufds[i].fd == get_property_set_fd()) 21 handle_property_set_fd(); // 处理设置属性的命令 22 else if (ufds[i].fd == get_keychord_fd()) 23 handle_keychord(); // 处理类似混合按键的命令,类似同时按 24 // 钢琴上的若干键 25 else if (ufds[i].fd == get_signal_fd()) 26 handle_signal(); // 处理因子进程挂掉而发来的信号 27 } 28 } 29 } 30 . . . . . . 31 }
在init进程的main()函数中,最终进入那个无限for循环,监听signal_recv_fd。
当监听到signal_recv_fd有数据传递过来时,会调用handle_signal()来处理:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#handle_signal
1 void handle_signal(void) 2 { 3 char tmp[32]; 4 5 /* we got a SIGCHLD - reap and restart as needed */ 6 read(signal_recv_fd, tmp, sizeof(tmp)); 7 while (!wait_for_one_process(0)) 8 ; 9 }
可以看到调用了wait_for_one_process()来处理:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#wait_for_one_process
1 static int wait_for_one_process(int block) 2 { 3 pid_t pid; 4 int status; 5 struct service *svc; 6 struct socketinfo *si; 7 time_t now; 8 struct listnode *node; 9 struct command *cmd; 10 11 while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); 12 if (pid <= 0) return -1; 13 INFO("waitpid returned pid %d, status = %08x\n", pid, status); 14 15 svc = service_find_by_pid(pid); 16 if (!svc) { 17 ERROR("untracked pid %d exited\n", pid); 18 return 0; 19 } 20 21 NOTICE("process '%s', pid %d exited\n", svc->name, pid); 22 23 if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { 24 kill(-pid, SIGKILL); 25 NOTICE("process '%s' killing any children in process group\n", svc->name); 26 } 27 28 /* remove any sockets we may have created */ 29 for (si = svc->sockets; si; si = si->next) { 30 char tmp[128]; 31 snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); 32 unlink(tmp); 33 } 34 35 svc->pid = 0; 36 svc->flags &= (~SVC_RUNNING); 37 38 /* oneshot processes go into the disabled state on exit, 39 * except when manually restarted. */ 40 if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) { 41 svc->flags |= SVC_DISABLED; 42 } 43 44 /* disabled and reset processes do not get restarted automatically */ 45 if (svc->flags & (SVC_DISABLED | SVC_RESET) ) { 46 notify_service_state(svc->name, "stopped"); 47 return 0; 48 } 49 50 now = gettime(); 51 if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) { 52 if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { 53 if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { 54 ERROR("critical process '%s' exited %d times in %d minutes; " 55 "rebooting into recovery mode\n", svc->name, 56 CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); 57 android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); 58 return 0; 59 } 60 } else { 61 svc->time_crashed = now; 62 svc->nr_crashed = 1; 63 } 64 } 65 66 svc->flags &= (~SVC_RESTART); 67 svc->flags |= SVC_RESTARTING; 68 69 /* Execute all onrestart commands for this service. */ 70 list_for_each(node, &svc->onrestart.commands) { 71 cmd = node_to_item(node, struct command, clist); 72 cmd->func(cmd->nargs, cmd->args); 73 } 74 notify_service_state(svc->name, "restarting"); 75 return 0; 76 }
- 当产生信号的进程被终止时,waitpid()函数用来回收进程所占用的资源,它带有三个参数。其中,第一个参数pid为欲等待的子进程的识别码,设置为-1,表示查看所有子进程是否发出SIGCHLD信号;第二个参数status,用于返回子进程的结束状态;第三个参数决定waitpis()函数是否使用阻塞处理方式。waitpid()函数返回pid值,返回值即为产生SIGCHLD信号的进程的pid号。
- service_find_by_pid()函数用来根据pid取出与服务列表中终止进程相关的服务项目。
- 在取出的服务项目的选项中,检查SVC_ONESHOT是否已设置。SVC_ONESHOT表示进程仅运行一次,带有此选项的进程在运行一次后,不会被重新启动,由kill(-pid, SIGKILL)函数终止。
- 删除进程持有的所有socketDescriptor。
- SVC_RUNNING表示在服务项持有的pid值与状态标记中,进程处于驱动运行中。此段代码将删除SVC_RUNNING.
- SVC_ONESHOT选项将已设置进程标记为SVC_DISABLE,并从wait_for_one_process()函数中跳出,相关进程将不被重新启动
- 检查待重启的进程在init.rc文件中是否带有onrestart选项。onrestart选项是进程重启时待执行的命令。如下是带有onrestart选项的进程的示例:
1 service servicemanager /system/bin/servicemanager 2 class core 3 user system 4 group system 5 critical 6 onrestart restart healthd 7 onrestart restart zygote 8 onrestart restart media 9 onrestart restart surfaceflinger 10 onrestart restart drm
上述事例表示 servicemanager重启后,调用on_restart()函数,重启zygote / media等进程。
- 最后,向当前服务项的标记中添加SVC_RESTART。次标记被应用在restart_process()函数中,用来确定待重启的进程。
wait_for_one_process()里根本没有fork动作。这也就是说,wait_for_one_process()中并不会立即重启新的service进程。大家都知道现在我们正处于init进程的无限for循环中,所以程序从wait_for_one_process()返回后,总会再次走到for循环中的restart_processes():
1 int main(int argc, char **argv) 2 { 3 . . . . . 4 for(;;) { 5 int nr, i, timeout = -1; 6 execute_one_command(); 7 restart_processes(); 8 } 9 ........ 10 }
restart_processes()函数如下:
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#434
1 static void restart_processes() 2 { 3 process_needs_restart = 0; 4 service_for_each_flags(SVC_RESTARTING, 5 restart_service_if_needed); 6 }
restart_processes()函数检索出服务列表中带有SVC_RESTART标记的服务项,然后调用restart_service_if_needed()函数重启这个service 进程。
restart_service_if_needed()函数中调用了service_start()函数,进而调用fork创建新的service子进程。
3 总结
经过是上面的分析,我们已经对进程的终止与再重启的流程有了一个比较清晰的了解,可以大体总结如下图所示:
(1)注册信号处理函数,接收子进程终止时产生的SIGCHLD信号,调用sigchld_handler()函数进程处理:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#135
1 struct sigaction act; 2 memset(&act, 0, sizeof(act)); 3 act.sa_handler = sigchld_handler; 4 act.sa_flags = SA_NOCLDSTOP; 5 sigaction(SIGCHLD, &act, 0);
(2)创建socketpair,用于socket信息的发送与接收:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#142
1 /* create a signalling mechanism for the sigchld handler */ 2 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { 3 signal_fd = s[0]; 4 signal_recv_fd = s[1]; 5 fcntl(s[0], F_SETFD, FD_CLOEXEC); 6 fcntl(s[0], F_SETFL, O_NONBLOCK); 7 fcntl(s[1], F_SETFD, FD_CLOEXEC); 8 fcntl(s[1], F_SETFL, O_NONBLOCK); 9 }
(3)init进程的main()函数中监听SIGCHLD信号处理文件描述符:
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#1096
1 if (!signal_fd_init && get_signal_fd() > 0) { 2 ufds[fd_count].fd = get_signal_fd(); 3 ufds[fd_count].events = POLLIN; 4 ufds[fd_count].revents = 0; 5 fd_count++; 6 signal_fd_init = 1; 7 }
(4)poll()函数等待已注册的文件描述符发生事件,当有事件发生时,调用对应的处理函数,子进程终止时调用handle_signal():
http://androidxref.com/4.4_r1/xref/system/core/init/init.c#1131
1 nr = poll(ufds, fd_count, timeout); 2 if (nr <= 0) 3 continue; 4 5 for (i = 0; i < fd_count; i++) { 6 if (ufds[i].revents == POLLIN) { 7 if (ufds[i].fd == get_property_set_fd()) 8 handle_property_set_fd(); 9 else if (ufds[i].fd == get_keychord_fd()) 10 handle_keychord(); 11 else if (ufds[i].fd == get_signal_fd()) 12 handle_signal(); 13 } 14 }
(5)handle_signal()函数最终调用 wait_for_one_process()完成进程的重启:
http://androidxref.com/4.4_r1/xref/system/core/init/signal_handler.c#handle_signal
1 void handle_signal(void) 2 { 3 char tmp[32]; 4 5 /* we got a SIGCHLD - reap and restart as needed */ 6 read(signal_recv_fd, tmp, sizeof(tmp)); 7 while (!wait_for_one_process(0)) 8 ; 9 }
----------
=======
=======