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进程分析五 :进程的终止与再启动

  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 }
View Code

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 }
View Code

在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 }
View Code
  •  当产生信号的进程被终止时,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 }

 

 

 

----------

=======

=======

posted on 2020-01-17 17:16  二的次方  阅读(755)  评论(0编辑  收藏  举报