本章主要內(nèi)容
·? 深入分析init。
本章涉及的源代碼文件名及位置
下面是本章分析的源碼文件名及其位置。
·? init.c
system/core/init/init.c
·? parser.c
system/core/init/parser.c
·? builtins.c
system/core/init/builtins.c
·? keywords.h
system/core/init/keywords/h
·? init.rc
system/core/rootdir/init.rc
·? properties_service.c
system/core/init/properties_service.c
·? libc_init_dynamic.c
bionic/libc/bionic/libc_init_common.c
·? libc_init_common.c
bionic/libc/bionic/libc_init_common.c
·? properties.c
system/core/libcutils/properties.c
?init是一個(gè)進(jìn)程,確切地說,它是Linux系統(tǒng)中用戶空間的第一個(gè)進(jìn)程。由于Android是基于Linux內(nèi)核的,所以init也是Android系統(tǒng)中用戶空間的第一個(gè)進(jìn)程,它的進(jìn)程號(hào)是1。作為天字第一號(hào)的進(jìn)程,init被賦予了很多極其重要的工作職責(zé),本章將關(guān)注其中兩個(gè)比較重要的職責(zé):
·? init進(jìn)程負(fù)責(zé)創(chuàng)建系統(tǒng)中的幾個(gè)關(guān)鍵進(jìn)程,尤其是下一章要介紹的Zygote,它更是Java世界的開創(chuàng)者。那么,init進(jìn)程是如何創(chuàng)建Zygote的呢?
·? Android系統(tǒng)有很多屬性,于是init就提供了一個(gè)property service(屬性服務(wù))來管理它們。那么這個(gè)屬性服務(wù)是怎么工作的呢?
如上所述,本章將通過下面兩方面內(nèi)容來分析init:
·? init如何創(chuàng)建zygote。
·? init的屬性服務(wù)是如何工作的。
?
init進(jìn)程的入口函數(shù)是main,它的代碼如下所示:
[-->init.c]
int main(int argc, char **argv)
{
??? intdevice_fd = -1;
??? intproperty_set_fd = -1;
??? intsignal_recv_fd = -1;
??? intkeychord_fd = -1;
?? ?int fd_count;
??? ints[2];
??? intfd;
??? structsigaction act;
??? chartmp[PROP_VALUE_MAX];
??? structpollfd ufds[4];
??? char*tmpdev;
??? char*debuggable;
???
?? //設(shè)置子進(jìn)程退出的信號(hào)處理函數(shù),該函數(shù)為sigchld_handler。
???act.sa_handler = sigchld_handler;
??? act.sa_flags= SA_NOCLDSTOP;
???act.sa_mask = 0;
???act.sa_restorer = NULL;
???sigaction(SIGCHLD, &act, 0);
??
???......//創(chuàng)建一些文件夾,并掛載設(shè)備,這些是和Linux相關(guān)的,不擬做過多討論。
???mkdir("/dev/socket", 0755);
???mount("devpts", "/dev/pts", "devpts", 0,NULL);
???mount("proc", "/proc", "proc", 0, NULL);
???mount("sysfs", "/sys", "sysfs", 0, NULL);
?
??? //重定向標(biāo)準(zhǔn)輸入/輸出/錯(cuò)誤輸出到/dev/_null_。
open_devnull_stdio();
/*
設(shè)置init的日志輸出設(shè)備為/dev/__kmsg__,不過該文件打開后,會(huì)立即被unlink了,
這樣,其他進(jìn)程就無法打開這個(gè)文件讀取日志信息了。
*/
???log_init();
???
?? //上面涉及很多和Linux系統(tǒng)相關(guān)的知識(shí),不熟悉的讀者可自行研究,它們不影響我們的分析
?? //解析init.rc配置文件
???parse_config_file("/init.rc");
?
??? ......
?? ?//下面這個(gè)函數(shù)通過讀取/proc/cpuinfo得到機(jī)器的Hardware名,我的HTCG7手機(jī)為bravo。
???get_hardware_name();
snprintf(tmp,sizeof(tmp), "/init.%s.rc", hardware);
//解析這個(gè)和機(jī)器相關(guān)的配置文件,我的G7手機(jī)對(duì)應(yīng)文件為init.bravo.rc。
???parse_config_file(tmp);
?
/*
解析完上述兩個(gè)配置文件后,會(huì)得到一系列的Action(動(dòng)作),下面兩句代碼將執(zhí)行那些處于
early-init階段的Action。init將動(dòng)作執(zhí)行的時(shí)間劃分為四個(gè)階段:early-init、init、
early-boot、boot。由于有些動(dòng)作必須在其他動(dòng)作完成后才能執(zhí)行,所以就有了先后之分。哪些
動(dòng)作屬于哪個(gè)階段由配置文件決定。后面會(huì)介紹配置文件的相關(guān)知識(shí)。
*/
???action_for_each_trigger("early-init", action_add_queue_tail);
???drain_action_queue();
?
/*
創(chuàng)建利用Uevent和Linux內(nèi)核交互的socket。關(guān)于Uevent的知識(shí),第9章中對(duì)
Vold進(jìn)行分析時(shí)會(huì)做介紹。
??? */
???device_fd = device_init();
??? //初始化和屬性相關(guān)的資源
property_init();
//初始化/dev/keychord設(shè)備,這和調(diào)試有關(guān),本書不討論它的用法。讀者可以自行研究,
//內(nèi)容比較簡單。
???keychord_fd = open_keychord();
?
?? ?......
/*
? INIT_IMAGE_FILE定義為”/initlogo.rle”,下面這個(gè)函數(shù)將加載這個(gè)文件作為系統(tǒng)的開機(jī)
?畫面,注意,它不是開機(jī)動(dòng)畫控制程序bootanimation加載的開機(jī)動(dòng)畫文件。
*/
if(load_565rle_image(INIT_IMAGE_FILE) ) {
?? /*
如果加載initlogo.rle文件失?。赡苁菦]有這個(gè)文件),則會(huì)打開/dev/ty0設(shè)備,并
輸出”ANDROID”的字樣作為開機(jī)畫面。在模擬器上看到的開機(jī)畫面就是它。
*/
? ????......
??? ??}
?? }
?
??? if(qemu[0])
???????import_kernel_cmdline(1);
?? ......
//調(diào)用property_set函數(shù)設(shè)置屬性項(xiàng),一個(gè)屬性項(xiàng)包括屬性名和屬性值。
???property_set("ro.bootloader", bootloader[0] ? bootloader :"unknown");
?
??? ......//執(zhí)行位于init階段的動(dòng)作
???action_for_each_trigger("init", action_add_queue_tail);
???drain_action_queue();
?
?? ?//啟動(dòng)屬性服務(wù)
???property_set_fd = start_property_service();
?
/*
調(diào)用socketpair函數(shù)創(chuàng)建兩個(gè)已經(jīng)connect好的socket。socketpair是Linux的系統(tǒng)調(diào)用,
不熟悉的讀者可以利用man socketpair查詢相關(guān)信息。后面就會(huì)知道它們的用處了。
*/
??? if(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
???????signal_fd = s[0];
???????signal_recv_fd = s[1];
??????? ......
??? }
?
??? ......
?
??? //執(zhí)行配置文件中early-boot和boot階段的動(dòng)作。
???action_for_each_trigger("early-boot", action_add_queue_tail);
???action_for_each_trigger("boot", action_add_queue_tail);
???drain_action_queue();
......????
???
//init關(guān)注來自四個(gè)方面的事情。
??? ufds[0].fd= device_fd;//device_fd用于監(jiān)聽來自內(nèi)核的Uevent事件
???ufds[0].events = POLLIN;
???ufds[1].fd = property_set_fd;//property_set_fd用于監(jiān)聽來自屬性服務(wù)器的事件
ufds[1].events= POLLIN;
//signal_recv_fd由socketpair創(chuàng)建,它的事件來自另外一個(gè)socket。
???ufds[2].fd = signal_recv_fd;
???ufds[2].events = POLLIN;
???fd_count = 3;
if(keychord_fd > 0) {
?? //如果keychord設(shè)備初始化成功,則init也會(huì)關(guān)注來自這個(gè)設(shè)備的事件。
???????ufds[3].fd = keychord_fd;
???????ufds[3].events = POLLIN;
???????fd_count++;
}
......
?
#if BOOTCHART
??? ......//與Boot char相關(guān),不做討論了。
/*
Boot chart是一個(gè)小工具,它能對(duì)系統(tǒng)的性能進(jìn)行分析,并生成系統(tǒng)啟動(dòng)過程的圖表,
以提供一些有價(jià)值的信息,而這些信息最大的用處就是幫助提升系統(tǒng)的啟動(dòng)速度。
??? */
#endif
??for(;;) {
? ??????//從此init將進(jìn)入一個(gè)無限循環(huán)。
???????int nr, i, timeout = -1;
?
???????for (i = 0; i < fd_count; i++)
???????????ufds[i].revents = 0;
???????
??????? //在循環(huán)中執(zhí)行動(dòng)作
???????drain_action_queue();
???????restart_processes(); //重啟那些已經(jīng)死去的進(jìn)程
......
#if BOOTCHART
?? ?????...... // Boot Chart相關(guān)
#endif
??????? //調(diào)用poll等待一些事情的發(fā)生
??????? nr= poll(ufds, fd_count, timeout);
???????......
???????//ufds[2]保存的是signal_recv_fd,用于接收來自socket的消息。
??????? if(ufds[2].revents == POLLIN) {
???????????//有一個(gè)子進(jìn)程去世,init要處理這個(gè)事情
?????? ?????read(signal_recv_fd, tmp, sizeof(tmp));
???????????while (!wait_for_one_process(0))
???????????????;
???????????continue;
??????? }
?
??????? if(ufds[0].revents == POLLIN)
???????????handle_device_fd(device_fd);//處理Uevent事件
??????? if(ufds[1].revents == POLLIN)
???????????handle_property_set_fd(property_set_fd);//處理屬性服務(wù)的事件。
??????? if(ufds[3].revents == POLLIN)
???????????handle_keychord(keychord_fd);//處理keychord事件。
??? }
?
??? return0;
}
從上面的代碼中可知,init的工作任務(wù)還是很重的。上面的代碼雖已省略了不少行,可結(jié)果還是很長,不過從本章要分析的兩個(gè)知識(shí)點(diǎn)來看,可將init的工作流程精簡為以下四點(diǎn):
·? 解析兩個(gè)配置文件,其中,將分析對(duì)init.rc文件的解析。
·? 執(zhí)行各個(gè)階段的動(dòng)作,創(chuàng)建Zygote的工作就是在其中的某個(gè)階段完成的。
·? 調(diào)用property_init初始化屬性相關(guān)的資源,并且通過property_start_service啟動(dòng)屬性服務(wù)。
·? init進(jìn)入一個(gè)無限循環(huán),并且等待一些事情的發(fā)生。重點(diǎn)關(guān)注init如何處理來自socket和來自屬性服務(wù)器相關(guān)的事情。
精簡工作流程,是以后分析代碼時(shí)常用的方法。讀者在分析代碼的過程中,也可使用這種方法。
根據(jù)上面的代碼可知,在init中會(huì)解析兩個(gè)配置文件,其中一個(gè)是系統(tǒng)配置文件init.rc,另外一個(gè)是和硬件平臺(tái)相關(guān)的配置文件。以HTC G7手機(jī)為例,這個(gè)配置文件名為init.bravo.rc,其中bravo是硬件平臺(tái)的名稱。對(duì)這兩個(gè)配置文件進(jìn)行解析,調(diào)用的是同一個(gè)parse_config_file函數(shù)。下面就來看這個(gè)函數(shù),在分析過程中以init.rc為主。
[-->parser.c]
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);//讀取配置文件的內(nèi)容,這個(gè)文件是init.rc。
if (!data) return -1;
parse_config(fn,data); //調(diào)用parse_config做真正的解析
return 0;
}
讀取完文件的內(nèi)容后,將調(diào)用parse_config進(jìn)行解析,這個(gè)函數(shù)的代碼如下所示:
[-->parser.c]
static void parse_config(const char *fn, char*s)
{
struct parse_state state;
char *args[SVC_MAXARGS];
int nargs;
?
nargs = 0;
state.filename = fn;
state.line = 1;
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op; //設(shè)置解析函數(shù),不同的內(nèi)容用不同的解析函數(shù)
for (;;) {
??? switch(next_token(&state)) {
??? ??case T_EOF:
???????????state.parse_line(&state, 0, 0);
???????????return;
????? caseT_NEWLINE:
???????????if (nargs) {
??????????????//得到關(guān)鍵字的類型
???????????????int kw = lookup_keyword(args[0]);
???????????????if (kw_is(kw, SECTION)) { //判斷關(guān)鍵字類型是不是SECTION。
? ??????????????????state.parse_line(&state,0, 0);
?????????????????? parse_new_section(&state,kw, nargs, args);//解析這個(gè)SECTION。
???????????????} else {
???????????????????state.parse_line(&state, nargs, args);
???????????????}
???????????????nargs = 0;
???????????}
???????????break;
???????case T_TEXT:
??????????......
???????????break;
??????? }
??? }
}
上面就是parse_config函數(shù),代碼雖短,實(shí)際卻比較復(fù)雜。從整體來說,parse_config首先會(huì)找到配置文件的一個(gè)section,然后針對(duì)不同的 section使用不同的解析函數(shù)來解析。那么,什么是section呢?這和init.rc文件的組織結(jié)構(gòu)有關(guān)。先不必急著去看init.rc,還是先到代碼中去尋找答案。
keywords.h這個(gè)文件定義了init中使用的關(guān)鍵字,它的用法很有意思,先來看這個(gè)文件,代碼如下所示:
[-->keywords.h]
#ifndef KEYWORD?//如果沒有定義KEYWORD宏,則走下面的分支
......//聲明一些函數(shù),這些函數(shù)就是前面所說Action的執(zhí)行函數(shù)。
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
......
int do_restart(int nargs, char **args);
......
#define __MAKE_KEYWORD_ENUM__ ?//定義一個(gè)宏
/*
定義KEYWORD宏,雖然有四個(gè)參數(shù),不過這里只用第一個(gè),其中K_##symbol中的##表示連接
的意思,即最后得到的值為K_symbol。symbol其實(shí)就是init.rc中的關(guān)鍵字
*/
#define KEYWORD(symbol, flags, nargs, func)K_##symbol,
enum { //定義一個(gè)枚舉,這個(gè)枚舉定義了各個(gè)關(guān)鍵字的枚舉值。
???K_UNKNOWN,
#endif
......
//根據(jù)上面KEYWORD的定義,這里將得到一個(gè)枚舉值K_class,
???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(on,????????? SECTION, 0, 0)//K_on,
???KEYWORD(oneshot,???? OPTION,? 0, 0)
???KEYWORD(onrestart,?? OPTION,? 0, 0)
???KEYWORD(restart,???? COMMAND, 1,do_restart)
???KEYWORD(service,???? SECTION, 0,0)
?? ?......
???KEYWORD(socket,????? OPTION,? 0, 0)
???KEYWORD(start,?????? COMMAND, 1,do_start)
???KEYWORD(stop,??????? COMMAND, 1,do_stop)
??? ......
#ifdef __MAKE_KEYWORD_ENUM__
???KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD?//取消KEYWORD宏定義
#endif
keywords.h好像沒什么奇特,不過是個(gè)簡單的頭文件。為什么說它的用法很有意思呢?來看代碼中是如何使用它的,如下所示:
[-->parser.c]
......//parser.c中將包含keywords.h頭文件,而且還不只一次!!
//第一次包含keywords.h,根據(jù)keywords.h的代碼,我們首先會(huì)得到一個(gè)枚舉定義
#include "keywords.h"
/*
重新定義KEYWORD宏,這回四個(gè)參數(shù)全用上了,看起來好像是一個(gè)結(jié)構(gòu)體。其中#symbol表示
一個(gè)字符串,其值為“symbol”。
*/
#define KEYWORD(symbol, flags, nargs, func) \
??? [K_##symbol ] = { #symbol, func, nargs + 1, flags, },
?
//定義一個(gè)結(jié)構(gòu)體keyword_info數(shù)組,它用來描述關(guān)鍵字的一些屬性,請(qǐng)注意里面的注釋內(nèi)容。
struct {
??? constchar *name;? //關(guān)鍵字的名。
??? int(*func)(int nargs, char **args);//對(duì)應(yīng)關(guān)鍵字的處理函數(shù)。
unsignedchar nargs;//參數(shù)個(gè)數(shù),每個(gè)關(guān)鍵字的參數(shù)個(gè)數(shù)是固定的。
//關(guān)鍵字的屬性,有三種屬性,COMMAND、OPTION和SECTION。其中COMMAND有對(duì)應(yīng)的處理函數(shù)
???unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0},
/*
第二次包含keywords.h,由于已經(jīng)重新定了KEYWORD宏,所以以前那些作為枚舉值的關(guān)鍵字
現(xiàn)在變成keyword_info數(shù)組的索引了。
*/
#include "keywords.h"???
};
#undef KEYWORD
?
//一些輔助宏,幫助我們快速操作keyword_info中的內(nèi)容。
#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)
現(xiàn)在領(lǐng)略了keywords.h的神奇之處了吧?原來它干了兩件事情:
·? 第一次包含keyworks.h時(shí),它聲明了一些諸如do_classstart這樣的函數(shù),另外還定義了一個(gè)枚舉,枚舉值為K_class,K_mkdir等關(guān)鍵字。
·? 第二次包含keywords.h后,得到了一個(gè)keyword_info結(jié)構(gòu)體數(shù)組,這個(gè)keyword_info結(jié)構(gòu)體數(shù)組以前面定義的枚舉值為索引,存儲(chǔ)對(duì)應(yīng)的關(guān)鍵字信息,這些信息包括關(guān)鍵字名、處理函數(shù)、處理函數(shù)的參數(shù)個(gè)數(shù),以及屬性。
目前,關(guān)鍵字信息中最重要的就是symbol和flags了。什么樣的關(guān)鍵字被認(rèn)為是section呢?根據(jù)keywords.h的定義,symbol為下面兩個(gè)的關(guān)鍵字表示section:
KEYWORD(on,????????? SECTION, 0, 0)
KEYWORD(service,??? ?SECTION, 0, 0)
有了上面的知識(shí),再來看配置文件init.rc的內(nèi)容。
init.rc的內(nèi)容如下所示:(我們截取了部分內(nèi)容,注意,其中的注釋符號(hào)是#。)
[-->init.rc]
on init??#根據(jù)上面的分析,on關(guān)鍵字標(biāo)示一個(gè)section,對(duì)應(yīng)的名字是”init”
?......? #下面所有的內(nèi)容都屬于這個(gè)section,直到下一個(gè)section開始時(shí)。
?exportPATH /sbin:/system/sbin:/system/bin:/system/xbin
?exportLD_LIBRARY_PATH /system/lib
?exportANDROID_BOOTLOGO 1 #根據(jù)keywords.h的定義,export表示一個(gè)COMMAND
export ANDROID_ROOT /system
?exportANDROID_ASSETS /system/app
...... #省略部分內(nèi)容
on boot? #這是一個(gè)新的section,名為”boot”
?? ifup lo#這是一個(gè)COMMAND
???hostname localhost
???domainname localdomain
??? ......
?? #class_start也是一個(gè)COMMAND,對(duì)應(yīng)函數(shù)為do_class_start,很重要,切記。
??? class_startdefault?
??? ......
#下面這個(gè)section的意思是:待屬性persist.service.adb.enable的值變?yōu)?后,
#需要執(zhí)行對(duì)應(yīng)的COMMAND,這個(gè)COMMAND是start adbd
???? onproperty:persist.service.adb.enable=1
??? ?????start adbd?//start是一個(gè)COMMAND
??? ?on property:persist.service.adb.enable=0
? ???????stopadbd
??? ......
#service也是section的標(biāo)示,對(duì)應(yīng)section的名為“zygote“
service zygote /system/bin/app_process -Xzygote/system/bin –zygote ???????\
?--start-system-server
??? socketzygote stream 666? #socket關(guān)鍵字表示OPTION
???onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
???onrestart write /sys/power/state on
???onrestart restart media
#一個(gè)section,名為”media”
service media /system/bin/mediaserver
??? usermedia
??? groupsystem audio camera graphics inet net_bt net_bt_admin net_raw
iopriort 4
從上面對(duì)init.rc的分析中可知:
·? 一個(gè)section的內(nèi)容從這個(gè)標(biāo)示section的關(guān)鍵字開始,到下一個(gè)標(biāo)示section的地方結(jié)束。
·? init.rc中出現(xiàn)了名為boot和init的section,這里的boot和init,就是前面介紹的動(dòng)作執(zhí)行四個(gè)階段中的boot和init。也就是說,在boot階段執(zhí)行的動(dòng)作都是由boot這個(gè)section定義的。
另外還可發(fā)現(xiàn),zygote被放在了一個(gè)servicesection中。下面以zygote這個(gè)section為例,介紹service是如何解析的。
zygote對(duì)應(yīng)的service section內(nèi)容是:
[-->init.rc::zygote]
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \ --start-system-server
socketzygote stream 666? #socket是OPTION
#下面的onrestart是OPTION,而write和restart是COMMAND
??? onrestartwrite /sys/android_power/request_state wake
???onrestart write /sys/power/state on
onrestartrestart media
解析section的入口函數(shù)是parse_new_section,它的代碼如下所示:
[-->parser.c]
void parse_new_section(struct parse_state*state, int kw,
?????????????????????? int nargs, char **args)
{
???switch(kw) {
??? caseK_service:? //解析service,用parse_service和parse_line_service
???????state->context = parse_service(state, nargs, args);
??????? if(state->context) {
???????????state->parse_line = parse_line_service;
???? ???????return;
??????? }
???????break;
??? caseK_on: //解析on section
??????? ......//讀者可以自己研究
???????break;
??? }
???state->parse_line = parse_line_no_op;
}
其中,service解析時(shí),用到了parse_service和parse_line_service兩個(gè)函數(shù),在分別介紹它們之前,先看init是如何組織這個(gè)service的。
init中使用了一個(gè)叫service的結(jié)構(gòu)體來保存和service section相關(guān)的信息,不妨來看這個(gè)結(jié)構(gòu)體,代碼如下所示:
[-->init.h::service結(jié)構(gòu)體定義]
struct service {
?//listnode是一個(gè)特殊的結(jié)構(gòu)體,在內(nèi)核代碼中用得非常多,主要用來將結(jié)構(gòu)體鏈接成一個(gè)
? //雙向鏈表。init中有一個(gè)全局的service_list,專門用來保存解析配置文件后得到的service。
? ?struct listnode slist;?
??? constchar *name; //service的名字,對(duì)應(yīng)我們這個(gè)例子就是”zygote”。
??? constchar *classname; //service所屬class的名字,默認(rèn)是”defult”
???unsigned flags;//service的屬性
??? pid_tpid;??? //進(jìn)程號(hào)
??? time_ttime_started;?? //上一次啟動(dòng)的時(shí)間
??? time_ttime_crashed;? //上一次死亡的時(shí)間
??? intnr_crashed;????? ??//死亡次數(shù)
???? uid_tuid;???? //uid,gid相關(guān)
??? gid_tgid;
??? gid_tsupp_gids[NR_SVC_SUPP_GIDS];
??? size_tnr_supp_gids;
?? /*
有些service需要使用socket,下面這個(gè)socketinfo用來描述socket的相關(guān)信息。
我們的zygote也使用了socket,配置文件中的內(nèi)容是socket zygote stream 666。
它表示將創(chuàng)建一個(gè)AF_STREAM類型的socket(其實(shí)就是TCP socket),該socket的名為“zygote”,
讀寫權(quán)限是666。
?? */
structsocketinfo *sockets;?
//service一般運(yùn)行在單獨(dú)的一個(gè)進(jìn)程中,envvars用來描述創(chuàng)建這個(gè)進(jìn)程時(shí)所需的環(huán)境變量信息。
??? structsvcenvinfo *envvars;?
?? /*
? 雖然關(guān)鍵字onrestart標(biāo)示一個(gè)OPTION,可是這個(gè)OPTION后面一般跟著COMMAND,
?下面這個(gè)action結(jié)構(gòu)體可用來存儲(chǔ)command信息,馬上就會(huì)分析到它。
*/
??? structaction onrestart;
???
??? //和keychord相關(guān)的內(nèi)容
??? int*keycodes;
??? intnkeycodes;
??? intkeychord_id;
??? //io優(yōu)先級(jí)設(shè)置
??? intioprio_class;
??? intioprio_pri;
??? //參數(shù)個(gè)數(shù)
??? intnargs;
??? //用于存儲(chǔ)參數(shù)
??? char*args[1];
};?
我們現(xiàn)在已了解的service的結(jié)構(gòu)體,相對(duì)來說還算是清晰易懂的。而zygote中的那三個(gè)onrestart該怎么表示呢?請(qǐng)看service中使用的這個(gè)action結(jié)構(gòu)體:
[-->init.h::action結(jié)構(gòu)體定義]
struct action {
/*
一個(gè)action結(jié)構(gòu)體可存放在三個(gè)雙向鏈表中,其中alist用于存儲(chǔ)所有action,
qlist用于鏈接那些等待執(zhí)行的action,tlist用于鏈接那些待某些條件滿足后
就需要執(zhí)行的action。
*/
??? structlistnode alist;
?? structlistnode qlist;
??? structlistnode tlist;
?
???unsigned hash;
??? constchar *name;
???
?? //這個(gè)OPTION對(duì)應(yīng)的COMMAND鏈表,以zygote為例,它有三個(gè)onrestart option,所以
? //它對(duì)應(yīng)會(huì)創(chuàng)建三個(gè)command結(jié)構(gòu)體。
??? structlistnode commands;
??? structcommand *current;
};
了解了上面的知識(shí)后,你是否能猜到parse_service和parse_line_service的作用了呢?馬上就來看它們。
parse_service的代碼如下所示:
[-->parser.c]
static void *parse_service(struct parse_state*state, int nargs, char **args)
{
??? structservice *svc; //聲明一個(gè)service結(jié)構(gòu)體
??? ......
??? //init維護(hù)了一個(gè)全局的service鏈表,先判斷是否已經(jīng)有同名的service了。
??? svc =service_find_by_name(args[1]);
??? if(svc) {
?????? ......? //如果有同名的service,則不能繼續(xù)后面的操作。
???????return 0;
??? }
???
nargs-= 2;
??? svc =calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
??? ......
???svc->name = args[1];
svc->classname= "default";//設(shè)置classname為”default”,這個(gè)很關(guān)鍵!
???memcpy(svc->args, args + 2, sizeof(char*) * nargs);
???svc->args[nargs] = 0;
???svc->nargs = nargs;
svc->onrestart.name= "onrestart";
?
???list_init(&svc->onrestart.commands);
??? //把zygote這個(gè)service加到全局鏈表service_list中。
???list_add_tail(&service_list, &svc->slist);
??? returnsvc;
}
parse_service函數(shù)只是搭建了一個(gè)service的架子,具體的內(nèi)容尚需由后面的解析函數(shù)來填充。來看service的另外一個(gè)解析函數(shù)parse_line_service。
parse_line_service的代碼如下所示:
[-->parser.c]
static void parse_line_service(structparse_state *state, int nargs,
char **args)
{
??? structservice *svc = state->context;
??? structcommand *cmd;
??? int i,kw, kw_nargs;
??? ......
???svc->ioprio_class = IoSchedClass_NONE;
??? //其實(shí)還是根據(jù)關(guān)鍵字來做各種處理。
??? kw =lookup_keyword(args[0]);
??? switch(kw) {
??? caseK_capability:
???????break;
??? caseK_class:
??????? if(nargs != 2) {
???????????......
??????? }else {
???????????svc->classname = args[1];
??????? }
???????break;
??? ......
caseK_oneshot:
?? /*
這是service的屬性,它一共有五個(gè)屬性,分別為:
SVC_DISABLED:不隨class自動(dòng)啟動(dòng)。下面將會(huì)看到class的作用。
SVC_ONESHOT:退出后不需要重啟,也就是這個(gè)service只啟動(dòng)一次就可以了。
SVC_RUNNING:正在運(yùn)行,這是service的狀態(tài)。
SVC_RESTARTING:等待重啟,這也是service的狀態(tài)。
SVC_CONSOLE:該service需要使用控制臺(tái) 。
SVC_CRITICAL:如果在規(guī)定時(shí)間內(nèi)該service不斷重啟,則系統(tǒng)會(huì)重啟并進(jìn)入恢復(fù)模式。
zygote沒有使用任何屬性,這表明它:會(huì)隨著class的處理自動(dòng)啟動(dòng);
退出后會(huì)由init重啟;不使用控制臺(tái);即使不斷重啟也不會(huì)導(dǎo)致系統(tǒng)進(jìn)入恢復(fù)模式。
?????? */
???????svc->flags |= SVC_ONESHOT;
???????break;
??? caseK_onrestart: //根據(jù)onrestart的內(nèi)容,填充action結(jié)構(gòu)體的內(nèi)容
???????nargs--;
???????args++;
??????? kw= lookup_keyword(args[0]);
??????? ......
??????? //創(chuàng)建command結(jié)構(gòu)體
???????cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
???????cmd->func = kw_func(kw);
???????cmd->nargs = nargs;
???????memcpy(cmd->args, args, sizeof(char*) * nargs);
??????? //把新建的command加入到雙向鏈表中。
???????list_add_tail(&svc->onrestart.commands, &cmd->clist);
???????break;
??? ......
??? caseK_socket: { //創(chuàng)建socket相關(guān)信息
???????struct socketinfo *si;
??????? ......
??????? 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的讀寫權(quán)限
??????? 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將根據(jù)配置文件的內(nèi)容填充service結(jié)構(gòu)體,那么,zygote解析完后會(huì)得到什么呢?圖3-1表示了zygote解析后的結(jié)果:
http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter3/image001.png" alt="image" />
圖3-1? zygote解析結(jié)果示意圖
從上圖中可知:
·? service_list鏈表將解析后的service全部鏈接到了一起,并且是一個(gè)雙向鏈表,前向節(jié)點(diǎn)用prev表示,后向節(jié)點(diǎn)用next表示。
·? socketinfo也是一個(gè)雙向鏈表,因?yàn)閦ygote只有一個(gè)socket,所以畫了一個(gè)虛框socket做為鏈表的示范。
·? onrestart通過commands指向一個(gè)commands鏈表,zygote有三個(gè)commands。
zygote這個(gè)service解析完了,現(xiàn)在就是“萬事俱備,只欠東風(fēng)”了。接下來要了解的是,init是如何控制service的。
先看service是如何啟動(dòng)的。
init.rc中有這樣一句話:
#class_start是一個(gè)COMMAND,對(duì)應(yīng)的函數(shù)為do_class_start,很重要,切記。
?class_startdefault
class_start標(biāo)示一個(gè)COMMAND,對(duì)應(yīng)的處理函數(shù)為do_class_start,它位于boot section的范圍內(nèi)。為什么說它很重要呢?
還記得init進(jìn)程中的四個(gè)執(zhí)行階段嗎?當(dāng)init進(jìn)程執(zhí)行到下面幾句話時(shí),do_class_start就會(huì)被執(zhí)行了。
//將bootsection節(jié)的command加入到執(zhí)行隊(duì)列
action_for_each_trigger("boot",action_add_queue_tail);
//執(zhí)行隊(duì)列里的命令,class可是一個(gè)COMMAND,所以它對(duì)應(yīng)的do_class_start會(huì)被執(zhí)行。
drain_action_queue();
下面來看do_class_start函數(shù):
[-->builtins.c]
int do_class_start(int nargs, char **args)
{
/*
args為do_class_start的參數(shù),init.rc中只有一個(gè)參數(shù),就是default。
下面這個(gè)函數(shù)將從service_list中尋找classname為”default”的service,然后
調(diào)用service_start_if_not_disabled函數(shù)?,F(xiàn)在讀者明白了service結(jié)構(gòu)體中
classname的作用了嗎?
*/
service_for_each_class(args[1],service_start_if_not_disabled);
return 0;
}
我們已經(jīng)知道,zygote這個(gè)service的classname的值就是“default”,所以會(huì)針對(duì)這個(gè)service調(diào)用service_start_if_not_disabled,這個(gè)函數(shù)的代碼是:
[-->parser.c]
static void service_start_if_not_disabled(structservice *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
???? service_start(svc,NULL); //zygote可沒有設(shè)置SVC_DISABLED
?}
}
service_start函數(shù)的代碼如下所示:
[-->init.c]
void service_start(struct service *svc, constchar *dynamic_args)
{
??? structstat s;
??? pid_tpid;
??? intneeds_console;
??? int n;
?
???svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING));
???svc->time_started = 0;
???
??? if(svc->flags & SVC_RUNNING) {
???????return;//如果這個(gè)service已在運(yùn)行,則不用處理
??? }
? /*
service一般運(yùn)行于另外一個(gè)進(jìn)程中,這個(gè)進(jìn)程也是init的子進(jìn)程,所以啟動(dòng)service前需要判斷
對(duì)應(yīng)的可執(zhí)行文件是否存在,zygote對(duì)應(yīng)的可執(zhí)行文件是/system/bin/app_process
*/
??? if(stat(svc->args[0], &s) != 0) {
??????svc->flags |= SVC_DISABLED;
???????return;
??? }
? ??......
?? pid =fork(); //調(diào)用fork創(chuàng)建子進(jìn)程
if(pid == 0) {
??? //pid為零,我們?cè)谧舆M(jìn)程中
???????struct socketinfo *si;
???????struct svcenvinfo *ei;
???????char tmp[32];
???????int fd, sz;
???????
//得到屬性存儲(chǔ)空間的信息并加到環(huán)境變量中,后面在屬性服務(wù)一節(jié)中會(huì)碰到使用它的地方。
???????get_property_workspace(&fd, &sz);
???????add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
??????? //添加環(huán)境變量信息
???????for (ei = svc->envvars; ei; ei = ei->next)
???????????add_environment(ei->name, ei->value);
??????? //根據(jù)socketinfo創(chuàng)建socket
???????for (si = svc->sockets; si; si = si->next) {
???????????int s = create_socket(si->name,
? ????????????????????????????????!strcmp(si->type,"dgram") ?
????????????????????????????????? SOCK_DGRAM :SOCK_STREAM,
????????????????????????????????? si->perm,si->uid, si->gid);
???????????if (s >= 0) {
???????????????//在環(huán)境變量中添加socket信息。
???????? ???????publish_socket(si->name, s);
???????????}
??????? }
?????? ......//設(shè)置uid,gid等
?????setpgid(0, getpid());
?????? if(!dynamic_args) {
??????? /*
執(zhí)行/system/bin/app_process,這樣就進(jìn)入到app_process的main函數(shù)中了。
fork、execve這兩個(gè)函數(shù)都是Linux系統(tǒng)上常用的系統(tǒng)調(diào)用。
??????? */
?? ?????????if (execve(svc->args[0], (char**)svc->args, (char**) ENV) < 0) {
??????????????......
???????????}
??????? }else {
??????????......
??? }
?
?? ......//父進(jìn)程init的處理,設(shè)置service的信息,如啟動(dòng)時(shí)間、進(jìn)程號(hào),以及狀態(tài)等。
???svc->time_started = gettime();
???svc->pid = pid;
???svc->flags |= SVC_RUNNING;
//每一個(gè)service都有一個(gè)屬性,zygote的屬性為init.svc.zygote,現(xiàn)在設(shè)置它的值為running
???notify_service_state(svc->name, "running");
}
原來,zygote是通過fork和execv共同創(chuàng)建的!但service結(jié)構(gòu)中的那個(gè)onrestart好像沒有派上用場(chǎng),原因何在?
根據(jù)名字,就可猜到onrestart應(yīng)該是在zygote重啟時(shí)用的。下面先看在zygote死后,它的父進(jìn)程init會(huì)有什么動(dòng)作:
[-->init.c]
static void sigchld_handler(int s)
{? //當(dāng)子進(jìn)程退出時(shí),init的這個(gè)信號(hào)處理函數(shù)會(huì)被調(diào)用
???write(signal_fd, &s, 1); //往signal_fd write數(shù)據(jù)
}
signal_fd,就是在init中通過socketpair創(chuàng)建的兩個(gè)socket中的一個(gè),既然會(huì)往這個(gè)signal_fd中發(fā)送數(shù)據(jù),那么另外一個(gè)socket就一定能接收到,這樣就會(huì)導(dǎo)致init從poll函數(shù)中返回:
[-->init.rc::main函數(shù)代碼片斷]
?nr =poll(ufds, fd_count, timeout);
?......
?if(ufds[2].revents == POLLIN) {
???read(signal_recv_fd, tmp, sizeof(tmp));
???????while (!wait_for_one_process(0))//調(diào)用wait_for_one_process函數(shù)處理
???????? ;
???????continue;
?}
?......
//直接看這個(gè)wait_for_one_process函數(shù):
static int wait_for_one_process(int block)
{
??? pid_tpid;
??? intstatus;
??? structservice *svc;
??? structsocketinfo *si;
??? time_tnow;
??? structlistnode *node;
??? structcommand *cmd;
?
while( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 &&
errno == EINTR );
??? if(pid <= 0) return -1;
??? //找到死掉的那個(gè)service,現(xiàn)在應(yīng)該找到了代表zygote的那個(gè)service。
svc = service_find_by_pid(pid);
? ?......
?
if(!(svc->flags & SVC_ONESHOT)) {
??? //殺掉zygote創(chuàng)建的所有子進(jìn)程,這就是zygote死后,Java世界崩潰的原因。
???????kill(-pid, SIGKILL);
?? }
?
??? //清理socket信息,不清楚的讀者可以通過命令man 7 AF_UNIX查詢一下相關(guān)知識(shí)。
??? 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);
?
?? if(svc->flags & SVC_ONESHOT) {
???????svc->flags |= SVC_DISABLED;
??? }
? ?......
now= gettime();
/*
如果設(shè)置了SVC_CRITICAL標(biāo)示,則4分鐘內(nèi)該服務(wù)重啟次數(shù)不能超過4次,否則
機(jī)器會(huì)重啟進(jìn)入recovery模式。根據(jù)init.rc的配置,只有servicemanager進(jìn)程
享有此種待遇。
*/
??? if(svc->flags & SVC_CRITICAL) {
??????? if(svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
???????????if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
??????????????......
???????????????sync();
???????????????__reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,
????????????????????????LINUX_REBOOT_CMD_RESTART2, "recovery");
???????????????return 0;
???????????}
??????? }else {
???????????svc->time_crashed = now;
???????????svc->nr_crashed = 1;
??????? }
??? }
?
???svc->flags |= SVC_RESTARTING;
//設(shè)置標(biāo)示為SVC_RESTARTING,然后執(zhí)行該service onrestart中的COMMAND,這些內(nèi)容就
//非常簡單了,讀者可以自行學(xué)習(xí)。
???list_for_each(node, &svc->onrestart.commands) {
???????cmd = node_to_item(node, struct command, clist);
???????cmd->func(cmd->nargs, cmd->args);
}
//設(shè)置init.svc.zygote的值為restarting。
???notify_service_state(svc->name, "restarting");
??? return0;
}
通過上面的代碼,可知道onrestart的作用了,但zygote本身又在哪里重啟的呢?答案就在下面的代碼中:
[-->init.c::main函數(shù)代碼片斷]
for(;;) {
???????int nr, i, timeout = -1;
???????for (i = 0; i < fd_count; i++)
???????????ufds[i].revents = 0;
???????drain_action_queue(); //poll函數(shù)返回后,會(huì)進(jìn)入下一輪的循環(huán)
???????restart_processes(); //這里會(huì)重啟所有flag標(biāo)志為SVC_RESTARTING的service。
???????......
}
這樣,zygote又回來了!
我們知道,Windows平臺(tái)上有一個(gè)叫注冊(cè)表的東西。注冊(cè)表可以存儲(chǔ)一些類似key/value的鍵值對(duì)。一般而言,系統(tǒng)或某些應(yīng)用程序會(huì)把自己的一些屬性存儲(chǔ)在