二、Android情景分析之详解init进程(以启动zygote为例)
来源:互联网 发布:网络电视怎么看直播啊 编辑:程序博客网 时间:2024/05/21 20:29
概述
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进程的main函数
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.rc
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 服务
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的。
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
了解了关键的结构体之后,我们接下来看看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函数吧。
解析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。
init控制service
启动zygote
init.rc的zygote服务中有这样一句话: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字段好像没有派上用场,原因何在?我们接着往下看。
重启zygote
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();
- ….
- }
至此,死去的服务又开始重新启动了。
- 二、Android情景分析之详解init进程(以启动zygote为例)
- Android情景分析之详解init进程(以启动zygote为例)
- Android情景分析之深入解析zygote
- Android情景分析之深入解析zygote
- 读书:《Android系统源代码情景分析》- 简述Zygote进程和System进程的启动
- Android Framework学习(二)之Zygote进程启动解析
- Android之Zygote启动详解
- Android之Zygote启动详解
- android启动之init进程详解
- 三、Android情景分析之深入解析zygote
- Android系统启动流程(二)解析Zygote进程启动过程
- Android runtime机制(二)zygote进程的启动流程
- (N)Telephony分析(一)之zygote进程启动
- 4.1Android进程启动原理分析----Zygote
- Android -- 系统进程Zygote的启动分析
- Zygote进程启动详解
- Zygote进程源码分析之二
- Android启动流程分析(二) init进程的启动
- EXTERN C 关键字
- 验证码有几种?
- Spring事务配置的五种方式(转载)
- spring配置: Annotation vs XML
- 网络流EK算法
- 二、Android情景分析之详解init进程(以启动zygote为例)
- 对org.springframework.beans.CachedIntrospectionResults的再次解读
- 程序设计原则
- 分贝(dB)的概念辨析
- mysql两种递归查询效率比较
- DATEDIF函数
- Very Deep Convolutional Networks for Large-Scale Image Recognition
- Android中使用DownloadManager类来管理数据下载
- 化学结构NMR图谱及数据导入Word的方法