zoukankan      html  css  js  c++  java
  • 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
    ", pid, status);
    14 
    15     svc = service_find_by_pid(pid);
    16     if (!svc) {
    17         ERROR("untracked pid %d exited
    ", pid);
    18         return 0;
    19     }
    20 
    21     NOTICE("process '%s', pid %d exited
    ", 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
    ", 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
    ", 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 }

    ----------

    =======

    =======

  • 相关阅读:
    Linux服务器上监控网络带宽的18个常用命令
    centos性能监控系列三:监控工具atop详解
    centos性能监控系列二:Collectl初解
    centos查看硬件信息
    CentOS 6.4安装配置LAMP服务器(Apache+PHP5+MySQL)
    CentOS下如何完全卸载MySQL?卸载自带的mysql
    安全初始化MySql服务器
    用nginx-gridFS读取MongoDB的图片及文件(为什么你老是配不成功?)
    理解去中心化 稳定币 DAI
    如何开发一款以太坊(安卓)钱包系列2
  • 原文地址:https://www.cnblogs.com/roger-yu/p/12206743.html
Copyright © 2011-2022 走看看