init進程的入口函數是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;
//設置子進程退出的信號處理函數,該函數為sigchld_handler。
act.sa_handler = sigchld_handler;
act.sa_flags= SA_NOCLDSTOP;
act.sa_mask = 0;
act.sa_restorer = NULL;
sigaction(SIGCHLD, &act, 0);
......//創建一些文件夾,并掛載設備,這些是和Linux相關的,不擬做過多討論。
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0,NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
//重定向標準輸入/輸出/錯誤輸出到/dev/_null_。
open_devnull_stdio();
/*
設置init的日志輸出設備為/dev/__kmsg__,不過該文件打開后,會立即被unlink了,
這樣,其他進程就無法打開這個文件讀取日志信息了。
*/
log_init();
//上面涉及很多和Linux系統相關的知識,不熟悉的讀者可自行研究,它們不影響我們的分析
//解析init.rc配置文件
parse_config_file("/init.rc");
......
//下面這個函數通過讀取/proc/cpuinfo得到機器的Hardware名,我的HTCG7手機為bravo。
get_hardware_name();
snprintf(tmp,sizeof(tmp), "/init.%s.rc", hardware);
//解析這個和機器相關的配置文件,我的G7手機對應文件為init.bravo.rc。
parse_config_file(tmp);
/*
解析完上述兩個配置文件后,會得到一系列的Action(動作),下面兩句代碼將執行那些處于
early-init階段的Action。init將動作執行的時間劃分為四個階段:early-init、init、
early-boot、boot。由于有些動作必須在其他動作完成后才能執行,所以就有了先后之分。哪些
動作屬于哪個階段由配置文件決定。后面會介紹配置文件的相關知識。
*/
action_for_each_trigger("early-init", action_add_queue_tail);
drain_action_queue();
/*
創建利用Uevent和Linux內核交互的socket。關于Uevent的知識,第9章中對
Vold進行分析時會做介紹。
*/
device_fd = device_init();
//初始化和屬性相關的資源
property_init();
//初始化/dev/keychord設備,這和調試有關,本書不討論它的用法。讀者可以自行研究,
//內容比較簡單。
keychord_fd = open_keychord();
......
/*
INIT_IMAGE_FILE定義為”/initlogo.rle”,下面這個函數將加載這個文件作為系統的開機
畫面,注意,它不是開機動畫控制程序bootanimation加載的開機動畫文件。
*/
if(load_565rle_image(INIT_IMAGE_FILE) ) {
/*
如果加載initlogo.rle文件失敗(可能是沒有這個文件),則會打開/dev/ty0設備,并
輸出”ANDROID”的字樣作為開機畫面。在模擬器上看到的開機畫面就是它。
*/
......
}
}
if(qemu[0])
import_kernel_cmdline(1);
......
//調用property_set函數設置屬性項,一個屬性項包括屬性名和屬性值。
property_set("ro.bootloader", bootloader[0] ? bootloader :"unknown");
......//執行位于init階段的動作
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
//啟動屬性服務
property_set_fd = start_property_service();
/*
調用socketpair函數創建兩個已經connect好的socket。socketpair是Linux的系統調用,
不熟悉的讀者可以利用man socketpair查詢相關信息。后面就會知道它們的用處了。
*/
if(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
signal_fd = s[0];
signal_recv_fd = s[1];
......
}
......
//執行配置文件中early-boot和boot階段的動作。
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
drain_action_queue();
......
//init關注來自四個方面的事情。
ufds[0].fd= device_fd;//device_fd用于監聽來自內核的Uevent事件
ufds[0].events = POLLIN;
ufds[1].fd = property_set_fd;//property_set_fd用于監聽來自屬性服務器的事件
ufds[1].events= POLLIN;
//signal_recv_fd由socketpair創建,它的事件來自另外一個socket。
ufds[2].fd = signal_recv_fd;
ufds[2].events = POLLIN;
fd_count = 3;
if(keychord_fd > 0) {
//如果keychord設備初始化成功,則init也會關注來自這個設備的事件。
ufds[3].fd = keychord_fd;
ufds[3].events = POLLIN;
fd_count++;
}
......
#if BOOTCHART
......//與Boot char相關,不做討論了。
/*
Boot chart是一個小工具,它能對系統的性能進行分析,并生成系統啟動過程的圖表,
以提供一些有價值的信息,而這些信息最大的用處就是幫助提升系統的啟動速度。
*/
#endif
for(;;) {
//從此init將進入一個無限循環。
int nr, i, timeout = -1;
for (i = 0; i < fd_count; i++)
ufds[i].revents = 0;
//在循環中執行動作
drain_action_queue();
restart_processes(); //重啟那些已經死去的進程
......
#if BOOTCHART
...... // Boot Chart相關
#endif
//調用poll等待一些事情的發生
nr= poll(ufds, fd_count, timeout);
......
//ufds[2]保存的是signal_recv_fd,用于接收來自socket的消息。
if(ufds[2].revents == POLLIN) {
//有一個子進程去世,init要處理這個事情
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);//處理屬性服務的事件。
if(ufds[3].revents == POLLIN)
handle_keychord(keychord_fd);//處理keychord事件。
}
return0;
}
~~~
從上面的代碼中可知,init的工作任務還是很重的。上面的代碼雖已省略了不少行,可結果還是很長,不過從本章要分析的兩個知識點來看,可將init的工作流程精簡為以下四點:
- 解析兩個配置文件,其中,將分析對init.rc文件的解析。
- 執行各個階段的動作,創建Zygote的工作就是在其中的某個階段完成的。
- 調用property_init初始化屬性相關的資源,并且通過property_start_service啟動屬性服務。
- init進入一個無限循環,并且等待一些事情的發生。重點關注init如何處理來自socket和來自屬性服務器相關的事情。
>[info] **提示**:精簡工作流程,是以后分析代碼時常用的方法。讀者在分析代碼的過程中,也可使用這種方法。
- 前言
- 第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 本章小結