根據上面的代碼可知,在init中會解析兩個配置文件,其中一個是系統配置文件init.rc,另外一個是和硬件平臺相關的配置文件。以HTC G7手機為例,這個配置文件名為init.bravo.rc,其中bravo是硬件平臺的名稱。對這兩個配置文件進行解析,調用的是同一個parse_config_file函數。下面就來看這個函數,在分析過程中以init.rc為主。
**parser.c**
~~~
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);//讀取配置文件的內容,這個文件是init.rc。
if (!data) return -1;
parse_config(fn,data); //調用parse_config做真正的解析
return 0;
}
~~~
讀取完文件的內容后,將調用parse_config進行解析,這個函數的代碼如下所示:
**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; //設置解析函數,不同的內容用不同的解析函數
for (;;) {
switch(next_token(&state)) {
case T_EOF:
state.parse_line(&state, 0, 0);
return;
caseT_NEWLINE:
if (nargs) {
//得到關鍵字的類型
int kw = lookup_keyword(args[0]);
if (kw_is(kw, SECTION)) { //判斷關鍵字類型是不是SECTION。
state.parse_line(&state,0, 0);
parse_new_section(&state,kw, nargs, args);//解析這個SECTION。
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
break;
case T_TEXT:
......
break;
}
}
}
~~~
上面就是parse_config函數,代碼雖短,實際卻比較復雜。從整體來說,parse_config首先會找到配置文件的一個section,然后針對不同的 section使用不同的解析函數來解析。那么,什么是section呢?這和init.rc文件的組織結構有關。先不必急著去看init.rc,還是先到代碼中去尋找答案。
1. 關鍵字定義
keywords.h這個文件定義了init中使用的關鍵字,它的用法很有意思,先來看這個文件,代碼如下所示:
**keywords.h**
~~~
#ifndef KEYWORD //如果沒有定義KEYWORD宏,則走下面的分支
......//聲明一些函數,這些函數就是前面所說Action的執行函數。
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__ //定義一個宏
/*
定義KEYWORD宏,雖然有四個參數,不過這里只用第一個,其中K_##symbol中的##表示連接
的意思,即最后得到的值為K_symbol。symbol其實就是init.rc中的關鍵字
*/
#define KEYWORD(symbol, flags, nargs, func)K_##symbol,
enum { //定義一個枚舉,這個枚舉定義了各個關鍵字的枚舉值。
K_UNKNOWN,
#endif
......
//根據上面KEYWORD的定義,這里將得到一個枚舉值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好像沒什么奇特,不過是個簡單的頭文件。為什么說它的用法很有意思呢?來看代碼中是如何使用它的,如下所示:
**parser.c**
~~~
......//parser.c中將包含keywords.h頭文件,而且還不只一次!!
//第一次包含keywords.h,根據keywords.h的代碼,我們首先會得到一個枚舉定義
#include "keywords.h"
/*
重新定義KEYWORD宏,這回四個參數全用上了,看起來好像是一個結構體。其中#symbol表示
一個字符串,其值為“symbol”。
*/
#define KEYWORD(symbol, flags, nargs, func) \
[K_##symbol ] = { #symbol, func, nargs + 1, flags, },
//定義一個結構體keyword_info數組,它用來描述關鍵字的一些屬性,請注意里面的注釋內容。
struct {
constchar *name; //關鍵字的名。
int(*func)(int nargs, char **args);//對應關鍵字的處理函數。
unsignedchar 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
//一些輔助宏,幫助我們快速操作keyword_info中的內容。
#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)
~~~
現在領略了keywords.h的神奇之處了吧?原來它干了兩件事情:
- 第一次包含keyworks.h時,它聲明了一些諸如do_classstart這樣的函數,另外還定義了一個枚舉,枚舉值為K_class,K_mkdir等關鍵字。
- 第二次包含keywords.h后,得到了一個keyword_info結構體數組,這個keyword_info結構體數組以前面定義的枚舉值為索引,存儲對應的關鍵字信息,這些信息包括關鍵字名、處理函數、處理函數的參數個數,以及屬性。
目前,關鍵字信息中最重要的就是symbol和flags了。什么樣的關鍵字被認為是section呢?根據keywords.h的定義,symbol為下面兩個的關鍵字表示section:
~~~
KEYWORD(on, SECTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
~~~
有了上面的知識,再來看配置文件init.rc的內容。
2. init.rc的解析
init.rc的內容如下所示:(我們截取了部分內容,注意,其中的注釋符號是#。)
**init.rc**
~~~
on init #根據上面的分析,on關鍵字標示一個section,對應的名字是”init”
...... #下面所有的內容都屬于這個section,直到下一個section開始時。
exportPATH /sbin:/system/sbin:/system/bin:/system/xbin
exportLD_LIBRARY_PATH /system/lib
exportANDROID_BOOTLOGO 1 #根據keywords.h的定義,export表示一個COMMAND
export ANDROID_ROOT /system
exportANDROID_ASSETS /system/app
...... #省略部分內容
on boot #這是一個新的section,名為”boot”
ifup lo#這是一個COMMAND
hostname localhost
domainname localdomain
......
#class_start也是一個COMMAND,對應函數為do_class_start,很重要,切記。
class_startdefault
......
#下面這個section的意思是:待屬性persist.service.adb.enable的值變為1后,
#需要執行對應的COMMAND,這個COMMAND是start adbd
onproperty:persist.service.adb.enable=1
start adbd //start是一個COMMAND
on property:persist.service.adb.enable=0
stopadbd
......
#service也是section的標示,對應section的名為“zygote“
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
--start-system-server
socketzygote stream 666 #socket關鍵字表示OPTION
onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
onrestart write /sys/power/state on
onrestart restart media
#一個section,名為”media”
service media /system/bin/mediaserver
usermedia
groupsystem audio camera graphics inet net_bt net_bt_admin net_raw
iopriort 4
~~~
從上面對init.rc的分析中可知:
- 一個section的內容從這個標示section的關鍵字開始,到下一個標示section的地方結束。
- init.rc中出現了名為boot和init的section,這里的boot和init,就是前面介紹的動作執行四個階段中的boot和init。也就是說,在boot階段執行的動作都是由boot這個section定義的。
另外還可發現,zygote被放在了一個servicesection中。下面以zygote這個section為例,介紹service是如何解析的。
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結