init是linux系统中用户空间的第一个进程。由于Android是基于linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号为1。
作为系统中的第一个用户空间进程,init进程被赋予了很多及其重要的工作职责。
1. init进程创建系统中几个关键进程,例如zygote等。
2. Android系统有很多属性,于是init就提供了一个property service(属性服务)来管理它们。(这篇文章讲过 http://blog.csdn.net/hu3167343/article/details/38230271)
今天我们主要以第1点来详细说明下init进程的工作。
init进程的主要代码如下:
int main(int argc, char **argv) { int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; char tmp[32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; ... mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); ... // 属性服务初始化 property_init(); get_hardware_name(hardware, &revision); process_kernel_cmdline(); // selinux安全机制初始化 union selinux_callback cb; cb.func_log = klog_write; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. * This must happen before /dev is populated by ueventd. */ restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); if (!is_charger) property_load_boot_defaults(); // 解析init.rc配置文件 INFO("reading config file\n"); init_parse_config_file("/init.rc"); /* 解析完init.rc配置文件后,会得到一系列的Action,action_for_each_trigger函数用来执行early-init阶段的Action。 init将动作执行的时间划分为4个阶段:early-init、init、early-boot、boot。由于有些动作必须要在其他动作完成后才能执行,所以就有了先后之分。哪些动作属于哪个阶段由配置文件决定。 */ action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ // 执行init阶段的动作 action_for_each_trigger("init", action_add_queue_tail); /* skip mounting filesystems in charger mode */ if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail); } /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); // 执行early-boot和boot阶段的动作 if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); #if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif // 无限循环,用来处理各种消息 for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); // 重启那些已经死去的进程 // 用来监听属性设置服务的事件 if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 1; } if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) timeout = 0; #if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } } #endif nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; // 处理具体的消息 for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } return 0; }
从以上代码来看,init进程的工作量还是很大的,主要集中在如下几个事情:
1. 解析init.rc配置文件。
2. 执行各个阶段的动作,创建zygote的工作就是在其中的某个阶段完成的。
3. 初始化property service(属性服务)。
4. init进入一个无限循环,并且等到一些事情的发生。
init的main函数主要是解析了init.rc文件,然后执行相应的初始化操作。我们先来看看init_parse_config_file函数的实现:
int init_parse_config_file(const char *fn) { char *data; data = read_file(fn, 0); // 读取inic.rc文件的内容 if (!data) return -1; // 开始解析 parse_config(fn, data); DUMP(); return 0; } static void parse_config(const char *fn, char *s) { struct parse_state state; struct listnode import_list; struct listnode *node; char *args[INIT_PARSER_MAXARGS]; int nargs; nargs = 0; state.filename = fn; state.line = 0; state.ptr = s; state.nexttoken = 0; state.parse_line = parse_line_no_op; // 设置解析函数,在android4.1上此函数为空 list_init(&import_list); state.priv = &import_list; // 开始解析init.rc文件内容,以行为单位 for (;;) { switch (next_token(&state)) { case T_EOF: // 文件末尾 state.parse_line(&state, 0, 0); goto parser_done; case T_NEWLINE: // 新的行 state.line++; if (nargs) { int kw = lookup_keyword(args[0]); if (kw_is(kw, SECTION)) { state.parse_line(&state, 0, 0); parse_new_section(&state, kw, nargs, args); } else { state.parse_line(&state, nargs, args); } nargs = 0; } break; case T_TEXT: // 文本内容,设置为args参数 if (nargs < INIT_PARSER_MAXARGS) { args[nargs++] = state.text; } break; } } parser_done: list_for_each(node, &import_list) { struct import *import = node_to_item(node, struct import, list); int ret; INFO("importing '%s'", import->filename); ret = init_parse_config_file(import->filename); if (ret) ERROR("could not import file '%s' from '%s'\n", import->filename, fn); } }
再来看看keywords.h对init.rc文件中关键字的定义
KEYWORD(capability, OPTION, 0, 0) KEYWORD(chdir, COMMAND, 1, do_chdir) KEYWORD(chroot, COMMAND, 1, do_chroot) KEYWORD(class, OPTION, 0, 0) KEYWORD(class_start, COMMAND, 1, do_class_start) KEYWORD(class_stop, COMMAND, 1, do_class_stop) KEYWORD(class_reset, COMMAND, 1, do_class_reset) KEYWORD(console, OPTION, 0, 0) KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) KEYWORD(domainname, COMMAND, 1, do_domainname) KEYWORD(exec, COMMAND, 1, do_exec) KEYWORD(export, COMMAND, 2, do_export) KEYWORD(group, OPTION, 0, 0) KEYWORD(hostname, COMMAND, 1, do_hostname) KEYWORD(ifup, COMMAND, 1, do_ifup) KEYWORD(insmod, COMMAND, 1, do_insmod) KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) KEYWORD(rm, COMMAND, 1, do_rm) KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(seclabel, OPTION, 0, 0) KEYWORD(service, SECTION, 0, 0) KEYWORD(setcon, COMMAND, 1, do_setcon) KEYWORD(setenforce, COMMAND, 1, do_setenforce) KEYWORD(setenv, OPTION, 2, 0) KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) KEYWORD(setsebool, COMMAND, 2, do_setsebool) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) KEYWORD(swapon_all, COMMAND, 1, do_swapon_all) KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) KEYWORD(ioprio, OPTION, 0, 0)
Init.rc文件的主要内容如下(system\core\rootdir\init.rc):
# Copyright (C) 2012 The Android Open Source Project # # IMPORTANT: Do not create world writable files or directories. # This is a common source of Android security bugs. # import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /init.trace.rc # 根据上面的分析可知,on关键字标示一个section,对应的名字为early-init on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_adj -16 # Set the security context for the init process. # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 start ueventd # create mountpoints mkdir /mnt 0775 root system … # 又一个新的section,名为boot on boot ... # class_start是一个COMMAND,对应处理函数是do_class_start class_start core class_start main … # system server cannot write to /proc/sys files, so proxy it through init on property:sys.sysctl.extra_free_kbytes=* write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes} ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd class core critical seclabel u:r:ueventd:s0 … # 下面这个section的意思是,如果属性ro.kernel.qemu为1,那么执行相应的COMMAND ,# 即start adbd on property:ro.kernel.qemu=1 start adbd ... # service关键字也是一个SECTION,对应的SECTION名为zygote。 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd service drm /system/bin/drmserver class main user drm group drm system inet drmrpc ...
从上面对init.rc文件大致分析可知
1、 一个section的内容从这个标识section的关键字开始,到下一个标识section的地方结束。
2、 Init.rc中出现了名为early-init和boot的section,这里的early-init和boot就是前面介绍的4个动作执行阶段中的early-init和boot。也就是说在boot阶段执行的动作都是由boot这个section定义的。
另外,zygote被放在了一个service section中,下面以zygote这个section为例,具体介绍下service是如何解析的。
Zygote对应的service section内容如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd
解析section的入口函数是parse_new_section:
void parse_new_section(struct parse_state *state, int kw, int nargs, char **args) { printf("[ %s %s ]\n", args[0], nargs > 1 ? args[1] : ""); switch(kw) { case K_service: state->context = parse_service(state, nargs, args); if (state->context) { state->parse_line = parse_line_service; return; } break; case K_on: state->context = parse_action(state, nargs, args); if (state->context) { state->parse_line = parse_line_action; return; } break; case K_import: parse_import(state, nargs, args); break; } state->parse_line = parse_line_no_op; }
其中,解析service时,用到了parse_service和parse_line_service这两个函数,在介绍这两个函数之前,我们首先看看init是如何组织这个service的。
struct service { /* 用来连接所有的services。Init中有一个全局的service_list变量,专门用来保存解析配置文件后得到的service。 */ struct listnode slist; const char *name; // service的名字,在我们的例子中为zygote const char *classname; // service所属的class名字,默认为default unsigned flags; // service的属性 pid_t pid; // 进程号 time_t time_started; /* 上一次启动时间 */ time_t time_crashed; /* 上一次死亡时间*/ int nr_crashed; /* 死亡次数 */ uid_t uid; gid_t gid; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; char *seclabel; /* 有些service需要使用socket来通信,下面这个socketinfo用来描述socket的相关信息。 我们的zygote也使用了socket,配置文件中的内容是socket zygote stream 660 root system。 它表示将创建一个AF_STREAM类型的socket(其实就是TCP socket),该socket的名为zygote,读写权限是660。 */ struct socketinfo *sockets; // service一般运行在一个单独的进程中,envvars用来描述创建这个进程时所需的环境 // 变量信息。 struct svcenvinfo *envvars; /* 虽然onrestart关键字是一个OPTION,可是这个OPTION后面一般都跟着一个COMMAND,action结构体就是用来存储command信息的。 */ struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; int keychord_id; // io优先级 int ioprio_class; int ioprio_pri; int nargs; // 参数个数 /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1]; // 用于存储参数内容 };
了解了service结构,那么其中的action是怎么保存的呢,我们再来看看action结构体。
struct action { /* 一个action结构可以被链入三个不同的双向链表中,其中alist用来存储所有的action, qlist用来链接那些等待执行的action,tlist用来链接那些待某些条件满足后就需要执行的action。 */ /* node in list of all actions */ struct listnode alist; /* node in the queue of pending actions */ struct listnode qlist; /* node in list of actions for a trigger */ struct listnode tlist; unsigned hash; const char *name; // 名字一般为"onrestart" /* 一个command的结构列表,command结构如下: struct command { /* list of commands in an action */ struct listnode clist; int (*func)(int nargs, char **args); int nargs; char *args[1]; }; */ struct listnode commands; struct command *current; };
了解了关键的结构体之后,我们接下来看看parse_service函数的具体思路:
static void *parse_service(struct parse_state *state, int nargs, char **args) { struct service *svc; // ….省略参数检测 // 根据服务名在全局服务链表service_list里面查找,如果已经有了,则返回 svc = service_find_by_name(args[1]); if (svc) { parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); return 0; } // 分配service结构内存 nargs -= 2; svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); if (!svc) { parse_error(state, "out of memory\n"); return 0; } svc->name = args[1]; // 服务名,此处为zygote svc->classname = "default"; // 设置classname为default,此处很关键 memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 拷贝参数,此处拷贝的是字符指针 svc->args[nargs] = 0; svc->nargs = nargs; // 保存参数个数 svc->onrestart.name = "onrestart"; list_init(&svc->onrestart.commands); // 初始化action结构的commands链表 list_add_tail(&service_list, &svc->slist); // 将当前服务添加到service_list链表中 return svc; }
由上面分析可知,parse_service函数只是搭建了一个svc的框架,并最后把它链入了service_list链表。我们再来看看parse_line_service函数吧。
static void parse_line_service(struct parse_state *state, int nargs, char **args) { struct service *svc = state->context; struct command *cmd; int i, kw, kw_nargs; if (nargs == 0) { return; } svc->ioprio_class = IoSchedClass_NONE; kw = lookup_keyword(args[0]); switch (kw) { case K_capability: break; case K_class: // 用来修改classname if (nargs != 2) { parse_error(state, "class option requires a classname\n"); } else { svc->classname = args[1]; } break; … case K_oneshot: svc->flags |= SVC_ONESHOT; break; case K_onrestart: // 开始解析onrestart后面的COMMAND nargs--; args++; kw = lookup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { parse_error(state, "invalid command '%s'\n", args[0]); break; } kw_nargs = kw_nargs(kw); if (nargs < kw_nargs) { parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, kw_nargs > 2 ? "arguments" : "argument"); break; } // 分配command结构的内存,拷贝参数等 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); cmd->func = kw_func(kw); cmd->nargs = nargs; memcpy(cmd->args, args, sizeof(char*) * nargs); // 添加到commands链表中 list_add_tail(&svc->onrestart.commands, &cmd->clist); break; … case K_socket: {/* name type perm [ uid gid ] */ struct socketinfo *si; if (nargs < 4) { parse_error(state, "socket option requires name, type, perm arguments\n"); break; } if (strcmp(args[2],"dgram") && strcmp(args[2],"stream") && strcmp(args[2],"seqpacket")) { parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n"); break; } si = calloc(1, sizeof(*si)); if (!si) { parse_error(state, "out of memory\n"); break; } si->name = args[1]; // socket名字 si->type = args[2]; // socket类型 si->perm = strtoul(args[3], 0, 8); // socket读写权限 if (nargs > 4) si->uid = decode_uid(args[4]); if (nargs > 5) si->gid = decode_uid(args[5]); si->next = svc->sockets; svc->sockets = si; break; } … default: parse_error(state, "invalid option '%s'\n", args[0]); } }
parse_line_service函数将根据配置文件的内容填充service结构体,zygote service解析完之后的结果如下图所示:
解析完之后:
1. 全局的service_list链表将解析后的service全部链接到了一起。
2. socketinfo也是一个双向链表,这里链接了service用到的socket信息,zygote进程只有一个socket。
3. onrestart通过commands指向一个commands链表,zygote共有4个commands。
service zygote /system/bin/app_process-Xzygote /system/bin --zygote --start-system-server
class main
服务默认创建的时候classname为“default”,这里通过“class main”来修改classname为“main”。之后,我们再回过头去看init.rc中有这个一句话:
在init进程启动的boot阶段,有一句“class_start main”。而class_start是一个COMMAND,其对应的处理函数是do_class_start。接着来看看do_class_start函数吧:
int do_class_start(int nargs, char **args) { /* Args为do_class_start的参数,init.rc中只有一个参数,就是“main“、“core”等。 service_for_each_class函数就是从service_list链表中找到classname和参数一致的service, 然后调用service_start_if_not_disabled函数。 */ service_for_each_class(args[1], service_start_if_not_disabled); return 0; } void service_for_each_class(const char *classname, void (*func)(struct service *svc)) { struct listnode *node; struct service *svc; list_for_each(node, &service_list) { svc = node_to_item(node, struct service, slist); if (!strcmp(svc->classname, classname)) { func(svc); } } }
service_start_if_not_disabled函数具体做了什么呢?接着看:
static void service_start_if_not_disabled(struct service *svc) { // 如果flags不为SVC_DISABLED,那么开始启动服务 if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); } }
具体来看看服务的启动过程:
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; char *scon = NULL; int rc; svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); svc->time_started = 0; if (svc->flags & SVC_RUNNING) { return; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } /* Service一般运行于另外一个进程中,这个进程也是init的子进程,所以启动service前需要判断对应的可执行文件是否存在,zygote服务对应的可执行文件为/system/bin/app_process */ if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } // … 省略selinux相关的内容 // 通过fork创建一个子进程 pid = fork(); if (pid == 0) { // 在子进程中 struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); // 设置属性服务信息到环境变量 if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } // 设置属性进程的环境变量 for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); setsockcreatecon(scon); // 根据socketinfo创建socket,用于通信 for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid); if (s >= 0) { publish_socket(si->name, s); } } …. if (!dynamic_args) { /* 调用execve函数来执行/system/bin/app_process,这样就进入了app_process的main函数中了。 */ if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { … } _exit(127); } … // 父进程中设置子服务进程的信息,启动时间、pid等。 svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; // 如果属性服务已经初始化完毕了,那么就设置相应服务为running状态 if (properties_inited()) notify_service_state(svc->name, "running"); }
原来,zygote是通过fork和execv共同创建的,但是service结构中的onrestart字段好像没有派上用场,原因何在?我们接着往下看。
init进程在初始化的时候会调用signal_init函数来初始化一个socket对,主要用来父子进程间通信。
void signal_init(void) { int s[2]; struct sigaction act; memset(&act, 0, sizeof(act)); /* 父进程设置sa_flags为SA_NOCLDSTOP,表明只有子进程在退出时父进程才能收到SIGCHLD的信号,处理函数为sigchld_handler */ act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &act, 0); /* create a signalling mechanism for the sigchld handler */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { signal_fd = s[0]; signal_recv_fd = s[1]; fcntl(s[0], F_SETFD, FD_CLOEXEC); fcntl(s[0], F_SETFL, O_NONBLOCK); fcntl(s[1], F_SETFD, FD_CLOEXEC); fcntl(s[1], F_SETFL, O_NONBLOCK); } handle_signal(); }
因此,子进程在退出时,父进程收到SIGCHLD消息,此时,信号处理函数为sigchld_handler。
static void sigchld_handler(int s) { write(signal_fd, &s, 1); }
signal_fd被写入值之后,那么对应的signal_recv_fd便能收到消息了。处理逻辑在init进程main函数的最后面的for循环中进行处理:
int get_signal_fd() { return signal_recv_fd; } for (i = 0; i < fd_count; i++) { … else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } void handle_signal(void) { char tmp[32]; /* we got a SIGCHLD - reap and restart as needed */ read(signal_recv_fd, tmp, sizeof(tmp)); while (!wait_for_one_process(0)) ; } static int wait_for_one_process(int block) { pid_t pid; int status; struct service *svc; struct socketinfo *si; time_t now; struct listnode *node; struct command *cmd; // 获得退出进程的pid while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); if (pid <= 0) return -1; INFO("waitpid returned pid %d, status = %08x\n", pid, status); // 遍历service_list链表,找到退出的那个服务 svc = service_find_by_pid(pid); if (!svc) { ERROR("untracked pid %d exited\n", pid); return 0; } NOTICE("process '%s', pid %d exited\n", svc->name, pid); /* 如果不是SVC_ONESHOT即一次性启动的服务,那么就杀死该进程的所有子进程,这也是为什么zygote死后,java世界崩溃的原因。 */ if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { kill(-pid, SIGKILL); NOTICE("process '%s' killing any children in process group\n", svc->name); } /* 删除服务创建的socket信息*/ for (si = svc->sockets; si; si = si->next) { char tmp[128]; snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); unlink(tmp); } svc->pid = 0; svc->flags &= (~SVC_RUNNING); /* 如果服务进程的标志SVC_ONESHOT置位了,则表明此服务为一次性服务,死了之后就不需要重启,除非设置SVC_RESTART标志来手动重启该服务。 此类服务死了之后,就置为SVC_DISABLED */ if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) { svc->flags |= SVC_DISABLED; } /* 通知属性服务器,将该服务属性设置为stopped */ if (svc->flags & (SVC_DISABLED | SVC_RESET) ) { notify_service_state(svc->name, "stopped"); return 0; } now = gettime(); /* 如果进程带有SVC_CRITICA标志,那么此时如果进程在4分钟内,死了>4次,则重启进入到recovery模式。根据init.rc的配置可知,ueventd、healthd、servicemanager等服务享有此种特殊待遇。 */ if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) { if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { ERROR("critical process '%s' exited %d times in %d minutes; " "rebooting into recovery mode\n", svc->name, CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); return 0; } } else { svc->time_crashed = now; svc->nr_crashed = 1; } } svc->flags &= (~SVC_RESTART); svc->flags |= SVC_RESTARTING; /* Execute all onrestart commands for this service. Zygote将执行如下操作: write /sys/android_power/request_state wake write /sys/power/state on restart media restart netd */ list_for_each(node, &svc->onrestart.commands) { cmd = node_to_item(node, struct command, clist); cmd->func(cmd->nargs, cmd->args); } /* 重新通知属性服务器,将该服务属性设置为restarting */ notify_service_state(svc->name, "restarting"); return 0; }
通过上面的代码可以知道onrestart的作用了,但是上述代码只是设置了相应svc的一些状态,flag、time_crashed、nr_crashed等等。那么真正的svc启动是在哪里呢?在init进程的main函数中,有一个大的无限循环,代码如下:
for(;;) { int nr, i, timeout = -1; execute_one_command(); // 此处就是调用service_start函数来重启那些flag带有SVC_RESTARTING的服务进程 restart_processes(); …. }
至此,死去的服务又开始重新启动了。