Android 内核加载完成后,就会启动init
进程,init进程是Android系统用户空间的第一个进程。init程序放在系统根目录下,init进程代码位于源码的目录“system/core/init”下面。
下面我们来分析init进程的启动过程
进程init入口函数是main,具体实现文件的路径是:
system\core\init\init.c
分析main
函数:
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;
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
if (!strcmp(basename(argv[0]), "watchdogd"))
return watchdogd_main(argc, argv);
/* clear the umask */
umask(0);
/* Get the basic filesystem setup we need put
* together in the initramdisk on / and then we'll
* let the rc file figure out the rest.
*/
//创建用户空间文件目录,让rc能够分辩这些目录
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);
/* indicate that booting is in progress to background fw loaders, etc */
/* 检测/dev/.booting文件是否可读写和创建*/
close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
/* We must have some place other than / to create the
* device nodes for kmsg and null, otherwise we won't
* be able to remount / read-only later on.
* Now that tmpfs is mounted on /dev, we can actually
* talk to the outside world.
*/
//重定向标准输入输出,错误输出到"/dev/__null__"
open_devnull_stdio();
//设置Init日志输出设备为"/dev/_ksg_"
klog_init();
// 初始化属性
property_init();
//从/proc/cpuinfo文件中获取Hardware字段的值
get_hardware_name(hardware, &revision);
//处理内核命令行参数,主要是导入内核命令行参数和用属性值设置内核变量
process_kernel_cmdline();
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,如果被禁止,则直接返回
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();
INFO("reading config file\n");
//解析/init.rc文件
init_parse_config_file("/init.rc");
//解析完/init.rc文件后,会得到一系列Action,按顺序执行其中的Action.
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 */
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");
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
//进入无限循环,建立init的子进程(init是所有进程的父进程)
for(;;) {
int nr, i, timeout = -1;
//执行命令(子进程对应的命令),该函数会从Action队列里读一个action和command进行执行
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
//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)初始化
初始化主要包括建立“dev”,”/proc”等目录,初始化属性,执行init.rc等初始化文件中的action等
(2)使用for循环,无限建立子进程的过程。
在Linux系统中,init进程是所有进程的父进程。输入ps命令,所有进程都是由这个进程在for循环中创建的,如果这个进程崩溃,那么android系统无法正常运行
# 2.配置文件详解
在init进程中。配置文件是指文件init.rc,其路径是:
system\core\rootdir\init.rc
文件init.rc是一个可配置的初始化文件,相同目录下的其它rc:
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc
定制了厂商可以配置的额外的初始化配置信息,具体格式为:
/init.${ro.hardware}.rc
解析init.rc时,是按行读取的,#表示注释。
文件init.rc 包含4种状态类别,分别是Actions、Commands、Services和Options。当声明一个Service或者Action的时候,它将隐式声明1个section.它之后跟随的Command或者Option都将属于这个section。Action和Service不能够重名。
(1)Actions
Actions 就是在某种条件下触发的一系列的命令,通常有一个trigger,形式如下所示:
on <trigger>
<command>
<command>
(2)Service
Service 的结构如下所示:
service <name> <pathname> <argument>
<option>
<option>
(3)Option
Option 是 Service 的修饰词,主要包括如下选项
1. critical
表明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。
2. disabled
表明这个服务不会同与他同trigger (触发器)下的服务自动启动。该服务必须被明确的按名启动。
3. setenv <name>
在进程启动时将环境变量<name>设置为。
4. socket <name> [ [ ] ]
Create a unix domain socketnamed /dev/socket/<name> and pass
its fd to the launchedprocess. must be"dgram", "stream" or "seqpacket".
User and group default to0.
创建一个unix域的名为/dev/socket/<name> 的套接字,并传递它的文件描述符给已启动的进程。 必须是 "dgram","stream" 或"seqpacket"。用户和组默认是0。
5. user
在启动这个服务前改变该服务的用户名。此时默认为 root。
6. group [ ]*
在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),档案默认是root。
7. oneshot
服务退出时不重启。
8. class <name>
指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为"default"类服务。
9. onrestart
当服务重启,执行一个命令(下详)。
(4)trigger.
trigger支持的选项:
1. boot
这是init执行后第一个被触发Trigger,也就是在 /init.rc被装载之后执行该Trigger
2. <name>=<value>
当属性<name>被设置成<value>时被触发。例如,
on property:vold.decrypt=trigger_reset_main
class_reset main
3. device-added-<path>
当设备节点被添加时触发
4. device-removed-<path>
当设备节点被移除时添加
5. service-exited-<name>
会在一个特定的服务退出时触发
(5) command命令
1. exec <path> [<argument> ]*
创建和执行一个程序(<path>)。在程序完全执行前,init将会阻塞。由于它不是内置命令,应尽量避免使用exec ,它可能会引起init执行超时。
2. export <name> <value>
在全局环境中将 <name>变量的值设为<value>。(这将会被所有在这命令之后运行的进程所继承)
3. ifup <interface>
启动网络接口
4. import <filename>
指定要解析的其他配置文件。常被用于当前配置文件的扩展
5. hostname <name>
设置主机名
6. chdir <directory>
改变工作目录
7. chmod <octal-mode><path>
改变文件的访问权限
8. chown <owner><group> <path>
更改文件的所有者和组
9. chroot <directory>
改变处理根目录
10. class_start<serviceclass>
启动所有指定服务类下的未运行服务。
11 class_stop<serviceclass>
停止指定服务类下的所有已运行的服务。
12. domainname <name>
设置域名
13. insmod <path>
加载<path>指定的驱动模块
14. mkdir <path> [mode][owner] [group]
创建一个目录<path> ,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和 root组。
15. mount <type> <device> <dir> [<mountoption> ]*
试图在目录<dir>挂载指定的设备。<device> 可以是mtd@name的形式指定一个mtd块设备。<mountoption>包括 "ro"、"rw"、"re
16. setkey
保留,暂时未用
17. setprop <name><value>
将系统属性<name>的值设为<value>。
18. setrlimit <resource> <cur> <max>
设置<resource>的rlimit (资源限制)
19. start <service>
启动指定服务(如果此服务还未运行)。
20.stop<service>
停止指定服务(如果此服务在运行中)。
21. symlink <target> <path>
创建一个指向<path>的软连接<target>。
22. sysclktz <mins_west_of_gmt>
设置系统时钟基准(0代表时钟滴答以格林威治平均时(GMT)为准)
23. trigger <event>
触发一个事件。用于Action排队
24. wait <path> [<timeout> ]
等待一个文件是否存在,当文件存在时立即返回,或到<timeout>指定的超时时间后返回,如果不指定<timeout>,默认超时时间是5秒。
25. write <path> <string> [ <string> ]*
向<path>指定的文件写入一个或多个字符串。
Android系统系统根目录中有一个init.rc文件,它事实上只是位于内存中,如果想要修改它,必须修改Rom中的内核镜像(boot.img)
init中对init.rc
文件的解析,是通过init_parse_config_file
文件进行的:
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
调用 read_file
函数将init.rc
文件读入到char*数组中,再调用 parse_config
对其进行解析
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;
//初始化parse_state结构体
nargs = 0;
state.filename = fn;
state.line = 0;
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op;//空函数,无任何操作,在parse_new_section中会对赋具体操作
list_init(&import_list);
state.priv = &import_list;
// 开始获取每一个token,然后分析这些token,每一个token就是有空格、字表符和回车符分隔的字符串
for (;;) {
/* next_token函数相当于词法分析器 */
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)) {//查看读取的关键字是否是Section ,只有service和on满足
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {
state.parse_line(&state, nargs, args);//on 和 service对应不同的操作
}
nargs = 0;
}
break;
case T_TEXT:
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);
}
}
通过上面代码我们知道,在for循环中对init.rc的内容进行了解析,以一行一行的形式进行读取
一个完整的编译器(或解析器)最开始需要进行词法和语法分析,词法分析就是在源代码文件中挑出一个个的Token,也就是说,词法分析器的返回值是 Token,而语法分析器的输入就是词法分析器的输出。也就是说,语法分析器需要分析一个个的token,而不是一个个的字符。由于init解析语言很简 单,所以就将词法和语法分析器放到了一起。词法分析器就是next_token函数,而语法分析器就是T_NEWLINE分支中的代码。这些就清楚多了。 现在先看看next_token函数(在parser.c文件中实现)是如何获取每一个token的。
int next_token(struct parse_state *state)
{
char *x = state->ptr;
char *s;
if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}
for (;;) {
switch (*x) {
case 0:
state->ptr = x;
return T_EOF;
case '\n':
x++;
state->ptr = x;
return T_NEWLINE;
case ' ':
case '\t':
case '\r':
x++;
continue;
case '#':
while (*x && (*x != '\n')) x++;
if (*x == '\n') {
state->ptr = x+1;
return T_NEWLINE;
} else {
state->ptr = x;
return T_EOF;
}
default:
goto text;
}
}
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone;
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"':
x++;
for (;;) {
switch (*x) {
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++;
}
}
break;
case '\\':
x++;
switch (*x) {
case 0:
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
}
}
return T_EOF;
}
next_token函数的代码还是很多的,不过原理到很简单。就是逐一读取init.rc文件(还有import导入的初始化文件)的字符,并将 由空格、“/t”和“/r”分隔的字符串挑出来,并通过state->text返回。如果返回了正常的token,next_token函数就返回 T_TEXT。如果一行结束,就返回T_NEWLINE,如果init.rc文件的内容已读取完,就返回T_EOF。当返回T_NEWLINE时,开始语 法分析(由于init初始化语言是基于行的,所以语言分析实际上就是分析init.rc文件的每一行,只是这些行已经被分解成一个个token了)
现在回到parse_config函数,先看一下T_TEXT分支。该分支将获得的每一行的token都存储在args数组中。现在来看 T_NEWLINE分支。该分支的代码涉及到一个state.parse_line函数指针,该函数指针指向的函数负责具体的分析工作。但我们发现,一看 是该函数指针指向了一个空函数parse_line_no_op,实际上,一开始该函数指针什么都不做,只是为了使该函数一开始不至于为null,否则调 用出错。
(2)每读完一行内容换行到下一行时,使用函数lookup_keyword
分析已经读取的一行的第一个参数
int lookup_keyword(const char *s)
{
switch (*s++) {
case 'c':
if (!strcmp(s, "opy")) return K_copy;
if (!strcmp(s, "apability")) return K_capability;
if (!strcmp(s, "hdir")) return K_chdir;
if (!strcmp(s, "hroot")) return K_chroot;
if (!strcmp(s, "lass")) return K_class;
if (!strcmp(s, "lass_start")) return K_class_start;
if (!strcmp(s, "lass_stop")) return K_class_stop;
if (!strcmp(s, "lass_reset")) return K_class_reset;
if (!strcmp(s, "onsole")) return K_console;
if (!strcmp(s, "hown")) return K_chown;
if (!strcmp(s, "hmod")) return K_chmod;
if (!strcmp(s, "ritical")) return K_critical;
break;
case 'd':
if (!strcmp(s, "isabled")) return K_disabled;
if (!strcmp(s, "omainname")) return K_domainname;
break;
case 'e':
if (!strcmp(s, "xec")) return K_exec;
if (!strcmp(s, "xport")) return K_export;
break;
case 'g':
if (!strcmp(s, "roup")) return K_group;
break;
case 'h':
if (!strcmp(s, "ostname")) return K_hostname;
break;
case 'i':
if (!strcmp(s, "oprio")) return K_ioprio;
if (!strcmp(s, "fup")) return K_ifup;
if (!strcmp(s, "nsmod")) return K_insmod;
if (!strcmp(s, "mport")) return K_import;
break;
case 'k':
if (!strcmp(s, "eycodes")) return K_keycodes;
break;
case 'l':
if (!strcmp(s, "oglevel")) return K_loglevel;
if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
break;
case 'm':
if (!strcmp(s, "kdir")) return K_mkdir;
if (!strcmp(s, "ount_all")) return K_mount_all;
if (!strcmp(s, "ount")) return K_mount;
break;
case 'o':
if (!strcmp(s, "n")) return K_on;
if (!strcmp(s, "neshot")) return K_oneshot;
if (!strcmp(s, "nrestart")) return K_onrestart;
break;
case 'p':
if (!strcmp(s, "owerctl")) return K_powerctl;
case 'r':
if (!strcmp(s, "estart")) return K_restart;
if (!strcmp(s, "estorecon")) return K_restorecon;
if (!strcmp(s, "mdir")) return K_rmdir;
if (!strcmp(s, "m")) return K_rm;
break;
case 's':
if (!strcmp(s, "eclabel")) return K_seclabel;
if (!strcmp(s, "ervice")) return K_service;
if (!strcmp(s, "etcon")) return K_setcon;
if (!strcmp(s, "etenforce")) return K_setenforce;
if (!strcmp(s, "etenv")) return K_setenv;
if (!strcmp(s, "etkey")) return K_setkey;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
if (!strcmp(s, "etsebool")) return K_setsebool;
if (!strcmp(s, "ocket")) return K_socket;
if (!strcmp(s, "tart")) return K_start;
if (!strcmp(s, "top")) return K_stop;
if (!strcmp(s, "wapon_all")) return K_swapon_all;
if (!strcmp(s, "ymlink")) return K_symlink;
if (!strcmp(s, "ysclktz")) return K_sysclktz;
break;
case 't':
if (!strcmp(s, "rigger")) return K_trigger;
break;
case 'u':
if (!strcmp(s, "ser")) return K_user;
break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
if (!strcmp(s, "ait")) return K_wait;
break;
}
return K_UNKNOWN;
}
函数lookup_keyword
主要对每一行的第一个字符做case
判断,然后调用strcmp命令对比剩余的字符。对于service
和on
命令,lookup_keyword
函数返回K_service
和K_on
.然后使用kw_is(kw,SECTION)判断返回的kw是否属于SECTION类型。在文件init.rc中,只有service和on满足该类型。
(3)定义关键字
keyword.h文件中定义了init使用的关键字。
#\system\core\init\keword.h
#ifndef KEYWORD //如果没有定义KEYWORD宏,别走下面分支
....//声明一些函数,这些函数就是前面所说的Action的执行函数
int do_chroot(int nargs, char **args);
int do_chdir(int nargs, char **args);
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
...
int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__ //定义一个宏。
//定义KEYWORD宏,虽然有4个参数,但只用第1个,其中K_##symbol中的##表示连接的意思,
//最后得到的值就是K_symbol.symbol就是init.rc中的关键字。
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {//定义一个枚举,这个枚举定义了各个关键字的枚举值。
K_UNKNOWN,
#endif
//根据上面的KEWORD定义,这里将得到一个枚举值 K_class
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) //K_class_start
KEYWORD(class_stop, COMMAND, 1, do_class_stop) //K_class_stop
...
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
KEYWORD(ioprio, OPTION, 0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif
文件keywords.h在文件init_parser.c中被用到了两次,具体引用代码如下所示:
//第一次包含keyword.h,我们首先得到一个枚举定义.
#include "keywords.h"
/*
重新定义 KEYWORD宏,这四个参数全用上,看起来好像一个结构体,其中 #symbol表示一个字符串,其值为"symbol"
*/
#define KEYWORD(symbol, flags, nargs, func) \
[ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
//定义一个结构体keyword_info数组,它用来描述关键字关键字的一些属性
struct {
const char *name;//关键字的名称
int (*func)(int nargs, char **args);//对应关键字的处理函数
unsigned char nargs;//参数个数,每个关键字的参数个数是固定的
//关键字的属性有三种:COMMAND,OPTION和SECTION,其中COMMAND有对应的处理函数
unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
/*
第二次包含keywords.h,由于已经重新定义了KEYWORD,所以以前那些枚举值的关键字现在变成keyword_info数据的索引。
*/
#include "keywords.h"
};
#undef KEYWORD
#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
由前面的函数lookup_kyword
可知,在调用过程中中会对on
和service
所在的段进行进行解析,这里首先分析service
,在分析时以文件init.rc中的service zygote
为例。
在文件init.rc
中,zygote
对应的service section
的代码如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main #option 启动main类
socket zygote stream 660 root system #option 开启unix域的socket
onrestart write /sys/android_power/request_state wake # onrestart 是option ,后面的是command
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: //用parse_service和parse_line_service解析service
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
case K_on: //用parse_action和parse_line_action解析action
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;
}
1.service 结构体
init中使用了一个叫service的结构体来保存与service section相关的信息
#init.h
struct service {
/* list of all services */
//listnode是一个特殊的结构体,在内核代码中使用非常多,一般位于另一个结构体的首部
//主要用来将另一个结构体链接成一个双向链表。init中有一个全局的service_list,专门用来保存解析配置文件后得到的service.
struct listnode slist;
const char *name;//service 名称,与我们这个例子对应的就是"zygote"
const char *classname; // service 所属 class名称,zygote对应main,默认default
unsigned flags; //service 的属性
pid_t pid; //进程号
time_t time_started; /* time of last start */ //上次启动时间
time_t time_crashed; /* first crash within inspection window *///上次死亡时间
int nr_crashed; /* number of times crashed within window *///死亡次数
uid_t uid;//用户ID,组ID
gid_t gid;
gid_t supp_gids[NR_SVC_SUPP_GIDS];
size_t nr_supp_gids;
char *seclabel;
/*
有些service 要用到socket,socketinfo 描述 socketinfo 相关的信息,它是一个单向的链表
struct socketinfo {
struct socketinfo *next;
const char *name; //socket 名字 ,本例为zygote
const char *type; // 类型 本例为 AF_STREAM 其实就是TCP 类型
uid_t uid;
gid_t gid;
int perm; //读写权限,本例为660
};
*/
struct socketinfo *sockets;
//service一般运行在一个单独的进程中,envvars用来描述这个进程的环境变量信息,也是一个链表。
struct svcenvinfo *envvars;
//虽然 onrestart 是一个 option ,但option 后面是 COMMAND,action 可以用来保存commnand信息
struct action onrestart; /* Actions to execute on restart. */
/* keycodes for triggering this service via /dev/keychord */
int *keycodes;
int nkeycodes;
int keychord_id;
int ioprio_class;
int ioprio_pri;
int nargs;
/* "MUST BE AT THE END OF THE STRUCT" */
char *args[1];
}; /* ^-------'args' MUST be at the end of this struct! */
了解到service结构体后,后面4个onrestart该如何处理?下面查看action
结构体
struct action {
/*
一个action结构体可以存放在三个双向链表中,其中alist用于存储所有的action,
qlist 用于连接那些等待执行的 action,tlist用于链接那些等待某些条件满足后就需要执行的aciton
*/
/* 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;
//这个OPTION对应的是COMMAND链表,以zygote为例,它有三个onrestart option.所以它会对应常见三个command结构体
struct listnode commands;//commands结构体链表首指针
struct command *current;//指向当前commnand结构体
// struct command
// {
// /* list of commands in an action */
// struct listnode clist;
// int (*func)(int nargs, char **args);
// int nargs;
// char *args[1];
};
};
这样通过上述两个结构体对service进行了组织
在解析Service会用到两个函数,分别是parse_service和parse_line+_service
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc;//声明一个双向链表指针
if (nargs < 3) {
parse_error(state, "services must have a name and a program\n");
return 0;
}
if (!valid_name(args[1])) {
parse_error(state, "invalid service name '%s'\n", args[1]);
return 0;
}
//init 维护了一个全局的双向 service链表,先判断是否有同名的 service了
svc = service_find_by_name(args[1]);
if (svc) { //如果有同名的 service ,直接返回。
parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
return 0;
}
nargs -= 2; //参数个数 -2 ,即除去已经减去的前两项
svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); //申请堆空间,将返回的地址赋给 svc.
if (!svc) {
parse_error(state, "out of memory\n");
return 0;
}
svc->name = args[1]; //初始化 service结构体中name
svc->classname = "default"; //默认情况下设置 classname 为 default,结果很重要
memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 拷贝 剩余的参数到 args中
svc->args[nargs] = 0; //参数数组中最后一个参数置零
svc->nargs = nargs; //
svc->onrestart.name = "onrestart";
list_init(&svc->onrestart.commands);//初始化 onrestart action 结构中command 双向链表
list_add_tail(&service_list, &svc->slist); // 把zygote这个 service 结构体添加到錽service链表尾部。
return svc;
}
parse_service 函数只是搭建了一个service的骨架,处理了servie关键字所在行的内容。该section的剩余部分由解析函数parse_line_service
按行解析填充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:
if (nargs != 2) {
parse_error(state, "class option requires a classname\n");
} else {
svc->classname = args[1];
}
break;
case K_console:
svc->flags |= SVC_CONSOLE;
break;
case K_disabled:
svc->flags |= SVC_DISABLED;
svc->flags |= SVC_RC_DISABLED;
break;
case K_ioprio:
if (nargs != 3) {
parse_error(state, "ioprio optin usage: ioprio );
} else {
svc->ioprio_pri = strtoul(args[2], 0, 8);
if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
parse_error(state, "priority value must be range 0 - 7\n");
break;
}
if (!strcmp(args[1], "rt")) {
svc->ioprio_class = IoSchedClass_RT;
} else if (!strcmp(args[1], "be")) {
svc->ioprio_class = IoSchedClass_BE;
} else if (!strcmp(args[1], "idle")) {
svc->ioprio_class = IoSchedClass_IDLE;
} else {
parse_error(state, "ioprio option usage: ioprio );
}
}
break;
case K_group:
if (nargs < 2) {
parse_error(state, "group option requires a group id\n");
} else if (nargs > NR_SVC_SUPP_GIDS + 2) {
parse_error(state, "group option accepts at most %d supp. groups\n",
NR_SVC_SUPP_GIDS);
} else {
int n;
svc->gid = decode_uid(args[1]);
for (n = 2; n < nargs; n++) {
svc->supp_gids[n-2] = decode_uid(args[n]);
}
svc->nr_supp_gids = n - 2;
}
break;
case K_keycodes:
if (nargs < 2) {
parse_error(state, "keycodes option requires atleast one keycode\n");
} else {
svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
if (!svc->keycodes) {
parse_error(state, "could not allocate keycodes\n");
} else {
svc->nkeycodes = nargs - 1;
for (i = 1; i < nargs; i++) {
svc->keycodes[i - 1] = atoi(args[i]);
}
}
}
break;
case K_oneshot:
/*
这是service的属性,它一共有五个属性,分别为:
SVC_DISABLED:不随class 自动启动。下面将会看到class的作用
SVC_ONESHOT:退出后不需要重启,也就是说这个service只启动一次就可以了。
SVC_RUNNING:正在运行,这是service的状体
SVC_CONSOLE:该service需要使用控制台。
SVC_CRITICAL:如果在规定时间内核service不断重启,则系统会重启并进入恢复模式
退出后会由init重启;不使用控制台;即使不断重启也不会导致系统进入恢复模式。
*/
svc->flags |= SVC_ONESHOT;
break;
case K_onrestart: //根据onrestart的内容,填充 action 结构体的内容
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); // 根据关键字找到对应函数 ,本例中对应write
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
//将新建的command加入到双向链表中
list_add_tail(&svc->onrestart.commands, &cmd->clist);
break;
case K_critical:
svc->flags |= SVC_CRITICAL;
break;
case K_setenv: { /* name value */
struct svcenvinfo *ei;
if (nargs < 2) {
parse_error(state, "setenv option requires name and value arguments\n");
break;
}
ei = calloc(1, sizeof(*ei));
if (!ei) {
parse_error(state, "out of memory\n");
break;
}
ei->name = args[1];
ei->value = args[2];
ei->next = svc->envvars;
svc->envvars = ei;
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];
si->type = args[2];
si->perm = strtoul(args[3], 0, 8);
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;
}
case K_user:
if (nargs != 2) {
parse_error(state, "user option requires a user id\n");
} else {
svc->uid = decode_uid(args[1]);
}
break;
case K_seclabel:
if (nargs != 2) {
parse_error(state, "seclabel option requires a label string\n");
} else {
svc->seclabel = args[1];
}
break;
default:
parse_error(state, "invalid option '%s'\n", args[0]);
}
}
parse_action
和parse_line_action
函数对 on section 进行了解析,以 on boot为例进行解析
on boot
...
class_start main
parse_action 函数
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
struct action *act;
if (nargs < 2) {
parse_error(state, "actions must have a trigger\n");
return 0;
}
if (nargs > 2) {
parse_error(state, "actions may not have extra parameters\n");
return 0;
}
act = calloc(1, sizeof(*act));//创建 action结构体
act->name = args[1]; // boot action对应boot
list_init(&act->commands);//初始化 action结构体中的command结构体双向链表
list_init(&act->qlist); // 初始化action结构体中的qlist结构体双向链表
list_add_tail(&action_list, &act->alist); //将创建的 action 结构体添加到init维护的action_list中
/* XXX add to hash */
return act;
}
parse_line_action函数
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
struct command *cmd;
struct action *act = state->context;
int (*func)(int nargs, char **args);
int kw, n;
if (nargs == 0) {
return;
}
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) {
parse_error(state, "invalid command '%s'\n", args[0]);
return;
}
n = kw_nargs(kw);
if (nargs < n) {
parse_error(state, "%s requires %d %s\n", args[0], n - 1,
n > 2 ? "arguments" : "argument");
return;
}
cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);//创建command命令结构体,zygote 对应boot中class_start 命令
cmd->func = kw_func(kw); //class_start对应的处理函数是do_class_start
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs); //复制do_class_start 的参数 main
list_add_tail(&act->commands, &cmd->clist);
}
解析生成action_list 和 servie_list双向链表后,会调action_for_each_trigger
函数将将action_list中的action按触发器名添加到action_queue
待执行队列中。
action_for_each_trigger("boot", action_add_queue_tail);
action_for_each_trigger函数:
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act->name, trigger)) {
func(act);
}
}
}
action_add_queue_tail函数,将action添加到queue_list中。
void action_add_queue_tail(struct action *act)
{
if (list_empty(&act->qlist)) {
list_add_tail(&action_queue, &act->qlist);
}
}
接着进入最后的for循环,调用execute_one_command(void)
命令执行queue_list
中的action的command
.
void execute_one_command(void)
{
int ret;
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
cur_action = action_remove_queue_head();
cur_command = NULL;
if (!cur_action)
return;
INFO("processing action %p (%s)\n", cur_action, cur_action->name);
cur_command = get_first_command(cur_action);
} else {
cur_command = get_next_command(cur_action, cur_command);
}
if (!cur_command)
return;
ret = cur_command->func(cur_command->nargs, cur_command->args);
INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}
对于boot section 的 class_start main 命令,执行的是Builtins.c
文件中的do_classs_start函数
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
//下面这个函数将从servie_list中找到classname为main的service,调用service_start_if_not_disabled
//创建该service,zygote service的class_name 恰也为main,因此会在boot section被创建
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
zygote service的class_name 恰也为main,因此会,在boot section被创建,调用service_start_if_not_disabled
方法创建:
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {//如果该服务的标志位被禁止,则不执行
service_start(svc, NULL);
}
}
service_start
函数
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;
/* starting a service removes it from the disabled or reset
* state and immediately takes it out of the restarting
* state if it was in there
*/
svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
svc->time_started = 0;
/* running processes require no additional work -- if
* they're in the process of exiting, we've ensured
* that they will immediately restart on exit, unless
* they are ONESHOT
*/
//如果该服务已经运行,则直接返回
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;
}
if (is_selinux_enabled() > 0) {
if (svc->seclabel) {
scon = strdup(svc->seclabel);
if (!scon) {
ERROR("Out of memory while starting '%s'\n", svc->name);
return;
}
} else {
char *mycon = NULL, *fcon = NULL;
INFO("computing context for service '%s'\n", svc->args[0]);
rc = getcon(&mycon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
return;
}
rc = getfilecon(svc->args[0], &fcon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
freecon(mycon);
return;
}
rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
freecon(mycon);
freecon(fcon);
if (rc < 0) {
ERROR("could not get context while starting '%s'\n", svc->name);
return;
}
}
}
NOTICE("starting '%s'\n", svc->name);
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);
// 将属性空间信息以 key=value形式添加到环境变量中,环境变量为一个静态字符串数组
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
}
for (ei = svc->envvars; ei; ei = ei->next)
add_environment(ei->name, ei->value);//添加到环境变量中
setsockcreatecon(scon);
//在/dev/socket/目录下创建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));
//对于zygote服务,创建目录为/dev/socket/zygote,zygote的文件描述符
int s = create_socket(si->name, socket_type,
si->perm, si->uid, si->gid);
if (s >= 0) {
//将创建的socket的文件描述符以"ANDROID_SOCKET_socketname=fd"形式添加到环境变量中
publish_socket(si->name, s);
}
}
freecon(scon);
scon = NULL;
setsockcreatecon(NULL);
if (svc->ioprio_class != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
}
}
if (needs_console) {
setsid();
open_console();
} else {
zap_stdio();
}
#if 0
for (n = 0; svc->args[n]; n++) {
INFO("args[%d] = '%s'\n", n, svc->args[n]);
}
for (n = 0; ENV[n]; n++) {
INFO("env[%d] = '%s'\n", n, ENV[n]);
}
#endif
//设置uid,groupid
setpgid(0, getpid());
/* as requested, set our gid, supplemental gids, and uid */
if (svc->gid) {
if (setgid(svc->gid) != 0) {
ERROR("setgid failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->nr_supp_gids) {
if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
ERROR("setgroups failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->uid) {
if (setuid(svc->uid) != 0) {
ERROR("setuid failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->seclabel) {
if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
_exit(127);
}
}
if (!dynamic_args) {
//执行/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 {
char *arg_ptrs[INIT_PARSER_MAXARGS+1];
int arg_idx = svc->nargs;
char *tmp = strdup(dynamic_args);
char *next = tmp;
char *bword;
/* Copy the static arguments */
memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
while((bword = strsep(&next, " "))) {
arg_ptrs[arg_idx++] = bword;
if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
arg_ptrs[arg_idx] = '\0';
//执行/system/bin/app_process,进入到app_process main函数
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
}
_exit(127);
}
freecon(scon);
if (pid < 0) {
ERROR("failed to start '%s'\n", svc->name);
svc->pid = 0;
return;
}
//父进程init的处理,设置service 的信息,如启动时间、进程号、服务标志等
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;
//如果init已经初始化property_area空间,则设置sevice的属性为running。
//每个service对应一个属性,zygote对应的属性为init.svc.zygote
if (properties_inited())
notify_service_state(svc->name, "running");
}
当 init 函数解释完 init.rc 文本时,会调用queue_builtin_action
函数创建一个signal_init action.初始化
init的信号机制.
//该 action 会调用signal_init_action初始化init进程信号处理机制
queue_builtin_action(signal_init_action, "signal_init");
该action最终会进入到 signal_init函数进行初始化
void signal_init(void)
{
int s[2];
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
//设置信号处理函数。当子进程停止时,内核调用sigchld_handler方法处理该信号
sigaction(SIGCHLD, &act, 0);
//创造一对未命名的、相互连接的UNIX域套接字。当SIGCHLD到达内核时,会调用sigchld_handle函数往signal_fd Socket写入信息
/* 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();
}
一旦有进程终止,signal_handler函数会向 signal_fd 中socket发送数据,signal_recv_fd就会接收到。
这时poll函数会检测到signal_revv_fd 文件的变化返回去执行handle_signal函数,进入到wait_for_one_process
函数中
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;
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进程id
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);
if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
//杀掉死掉进程的所创建的所有子进程。
kill(-pid, SIGKILL);
NOTICE("process '%s' killing any children in process group\n", svc->name);
}
//清除服务创建的sockets信息
/* remove any sockets we may have created */
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);
/* oneshot processes go into the disabled state on exit,
* except when manually restarted. */
if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
/* disabled and reset processes do not get restarted automatically */
if (svc->flags & (SVC_DISABLED | SVC_RESET) ) {
notify_service_state(svc->name, "stopped");
return 0;
}
now = gettime();
//如果4分钟重启次数超过4次,则进入到recovery模式
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;
//设置标志位为SVC_RESTARTING,然后执行该 service onrestart中的命令
/* Execute all onrestart commands for this service. */
list_for_each(node, &svc->onrestart.commands) {
cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
//设置init.svc.service_name的值为 restarting
notify_service_state(svc->name, "restarting");
return 0;
}
在下次循环中,会在restart_processes()
方法中重启标志位SVC_RESTARTING
的服务
重要参考:
1.Android的init过程详解(一):
http://www.cnblogs.com/nokiaguy/archive/2013/04/14/3020774.html
2.《深入理解Android虚拟机》
3.《深入理解android 卷1》