Android 內核加載完成後,就會啓動init
進程,init進程是Android系統用戶空間的第一個進程。init程序放在系統根目錄下,init進程代碼位於源碼的目錄“system/core/init”下面。
下面我們來分析init進程的啓動過程
1. 分析入口函數
進程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
2.1 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><value>
在進程啓動時將環境變量<name>設置爲<value>。
4. socket <name><type> <perm> [ <user> [ <group> ] ]
Create a unix domain socketnamed /dev/socket/<name> and pass
its fd to the launchedprocess. <type> must be"dgram", "stream" or "seqpacket".
User and group default to0.
創建一個unix域的名爲/dev/socket/<name> 的套接字,並傳遞它的文件描述符給已啓動的進程。<type> 必須是 "dgram","stream" 或"seqpacket"。用戶和組默認是0。
5. user <username>
在啓動這個服務前改變該服務的用戶名。此時默認爲 root。
6. group <groupname> [<groupname> ]*
在啓動這個服務前改變該服務的組名。除了(必需的)第一個組名,附加的組名通常被用於設置進程的補充組(通過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)
2 分析init.rc 的過程
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':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> 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)
3.解析 Service
由前面的函數lookup_kyword
可知,在調用過程中中會對on
和service
所在的段進行進行解析,這裏首先分析service
,在分析時以文件init.rc中的service zygote
爲例。
1.Zygote 對應的 service section
在文件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進行了組織
2.函數parse_service和parse_line_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 <rt|be|idle> <ioprio 0-7>\n");
} 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 <rt|be|idle> <0-7>\n");
}
}
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]);
}
}
3. parse_action 和parse_line_action函數
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);
}
3.執行action list 隊列啓動,啓動 zygotservice
解析生成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");
}
4.Service重啓
當 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》