鍍金池/ 教程/ Android/ 第3章 ?深入理解init
第4章 ?深入理解 Zygote
第10章 深入理解MediaScanner
第3章 ?深入理解init
第8章 ?深入理解Surface系統(tǒng)
第5章 深入理解常見類
第7章 ?深入理解Audio系統(tǒng)
第一章 ?閱讀前的準(zhǔn)備工作
<span>第6章 深入理解Binder</span>
第9章 ?深入理解Vold和Rild
第2章? 深入理解JNI

第3章 ?深入理解init

本章主要內(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

3.1 ?概述

?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ù)是如何工作的。

?

3.2 ?init分析

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í)常用的方法。讀者在分析代碼的過程中,也可使用這種方法。

3.2.1 ?解析配置文件

根據(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,還是先到代碼中去尋找答案。

1. 關(guān)鍵字定義

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)容。

2. init.rc的解析

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是如何解析的。

3.2.2 ?解析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的。

1. service結(jié)構(gòu)體

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的作用了呢?馬上就來看它們。

2. parse_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。

3. 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的。

3.2.3 ?init控制service

先看service是如何啟動(dòng)的。

1.啟動(dòng)zygote

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),原因何在?

2. 重啟zygote

根據(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又回來了!

3.2.4 ?屬性服務(wù)

我們知道,Windows平臺(tái)上有一個(gè)叫注冊(cè)表的東西。注冊(cè)表可以存儲(chǔ)一些類似key/value的鍵值對(duì)。一般而言,系統(tǒng)或某些應(yīng)用程序會(huì)把自己的一些屬性存儲(chǔ)在