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();
….
}
至此,死去的服务又开始重新启动了。