Android系統啟動-Init篇
基於Android 6.0的原始碼剖析, 分析Android啟動過程程序號為1的init程序的工作內容
/system/core/init/
- init.cpp
- init_parser.cpp
- signal_handler.cpp
一、概述
init是Linux系統中使用者空間的第一個程序,程序號為1。Kernel啟動後,在使用者空間,啟動init程序,並呼叫init中的main()方法執行init程序的職責。對於init程序的功能分為4部分:
- 分析和執行所有的init.rc檔案;
- 生成裝置驅動節點; (通過rc檔案建立)
- 處理子程序的終止(signal方式);
- 提供屬性服務。
接下來從main()方法說起。
1.1 main
[-> init.cpp]
int main(int argc, char** argv) { ... umask(0); //設定檔案屬性0777 klog_init(); //初始化kernel log,位於裝置節點/dev/kmsg【見小節1.2】 klog_set_level(KLOG_NOTICE_LEVEL); //設定輸出的log級別 // 輸出init啟動階段的log NOTICE("init%s started!\n", is_first_stage ? "" : " second stage"); property_init(); //建立一塊共享的記憶體空間,用於屬性服務 signal_handler_init(); //初始化子程序退出的訊號處理過程【見小節2.1】 property_load_boot_defaults(); //載入default.prop檔案 start_property_service(); //啟動屬性伺服器(通過socket通訊)【5.1】 init_parse_config_file("/init.rc"); //解析init.rc檔案 //執行rc檔案中觸發器為 on early-init的語句 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"); // 螢幕上顯示Android靜態Logo 【見小節1.3】 queue_builtin_action(console_init_action, "console_init"); //執行rc檔案中觸發器為 on init的語句 action_for_each_trigger("init", action_add_queue_tail); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); char bootmode[PROP_VALUE_MAX]; //當處於充電模式,則charger加入執行佇列;否則late-init加入佇列。 if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("late-init", action_add_queue_tail); } //觸發器為屬性是否設定 queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); while (true) { if (!waiting_for_exec) { execute_one_command(); restart_processes(); //【見小節1.4】 } int timeout = -1; if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) { timeout = 0; } epoll_event ev; //迴圈 等待事件發生 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout)); if (nr == -1) { ERROR("epoll_wait failed: %s\n", strerror(errno)); } else if (nr == 1) { ((void (*)()) ev.data.ptr)(); } } return 0; }
1.2 log系統
此時android的log系統還沒有啟動,採用kernel的log系統,開啟的裝置節點/dev/kmsg, 那麼可通過cat /dev/kmsg
來獲取核心log。
接下來,設定log的輸出級別為KLOG_NOTICE_LEVEL(5),當log級別小於5時則會輸出到kernel log, 預設值為3.
#define KLOG_ERROR_LEVEL 3 #define KLOG_WARNING_LEVEL 4 #define KLOG_NOTICE_LEVEL 5 #define KLOG_INFO_LEVEL 6 #define KLOG_DEBUG_LEVEL 7 #define KLOG_DEFAULT_LEVEL 3 //預設為3
1.3 console_init_action
[-> init.cpp]
static int console_init_action(int nargs, char **args)
{
char console[PROP_VALUE_MAX];
if (property_get("ro.boot.console", console) > 0) {
snprintf(console_name, sizeof(console_name), "/dev/%s", console);
}
int fd = open(console_name, O_RDWR | O_CLOEXEC);
if (fd >= 0)
have_console = 1;
close(fd);
fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
if (fd >= 0) {
const char *msg;
msg = "\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n" // console is 40 cols x 30 lines
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
" A N D R O I D ";
write(fd, msg, strlen(msg));
close(fd);
}
return 0;
}
這便是開機顯示的底部帶ANDROID字樣的畫面。
1.4 restart_processes
[-> init.cpp]
static void restart_processes()
{
process_needs_restart = 0;
service_for_each_flags(SVC_RESTARTING,
restart_service_if_needed);
}
檢查service_list中的所有服務,對於帶有SVC_RESTARTING標誌的服務,則都會呼叫其相應的restart_service_if_needed。
static void restart_service_if_needed(struct service *svc)
{
time_t next_start_time = svc->time_started + 5;
if (next_start_time <= gettime()) {
svc->flags &= (~SVC_RESTARTING);
service_start(svc, NULL);
return;
}
if ((next_start_time < process_needs_restart) ||
(process_needs_restart == 0)) {
process_needs_restart = next_start_time;
}
}
之後再呼叫service_start來啟動服務。
二、訊號處理
在init.cpp的main()方法中,通過signal_handler_init()來初始化訊號處理過程。
主要工作:
- 初始化signal控制代碼;
- 迴圈處理子程序;
- 註冊epoll控制代碼;
- 處理子程序的終止;
2.1 signal_handler_init
[-> signal_handler.cpp]
void signal_handler_init() {
int s[2];
// 建立socket pair
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
exit(1);
}
signal_write_fd = s[0];
signal_read_fd = s[1];
//當捕獲訊號SIGCHLD,則寫入signal_write_fd
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIGCHLD_handler;
//SA_NOCLDSTOP使init程序只有在其子程序終止時才會受到SIGCHLD訊號
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
reap_any_outstanding_children(); 【見小節2.2】
register_epoll_handler(signal_read_fd, handle_signal); //【見小節2.3】
}
每個程序在處理其他程序傳送的signal訊號時都需要先註冊,當程序的執行狀態改變或終止時會產生某種signal訊號,init程序是所有使用者空間程序的父程序,當其子程序終止時產生SIGCHLD訊號,init程序呼叫訊號安裝函式sigaction(),傳遞引數給sigaction結構體,便完成訊號處理的過程。
這裡有兩個重要的函式:SIGCHLD_handler和handle_signal,如下:
//寫入資料
static void SIGCHLD_handler(int) {
//向signal_write_fd寫入1,直到成功為止
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
}
}
//讀取資料
static void handle_signal() {
char buf[32];
//讀取signal_read_fd資料,放入buf
read(signal_read_fd, buf, sizeof(buf));
reap_any_outstanding_children(); 【見流程3-1】
}
2.2 reap_any_outstanding_children
[-> signal_handler.cpp]
static void reap_any_outstanding_children() {
while (wait_for_one_process()) { }
}
static bool wait_for_one_process() {
int status;
//等待任意子程序,如果子程序沒有退出則返回0,否則則返回該子程序pid。
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
ERROR("waitpid failed: %s\n", strerror(errno));
return false;
}
service* svc = service_find_by_pid(pid); //根據pid查詢到相應的service
std::string name;
if (!svc) {
return true;
}
//當flags為RESTART,且不是ONESHOT時,先kill程序組內所有的子程序或子執行緒
if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
kill(-pid, SIGKILL);
}
//移除當前服務svc中的所有建立過的socket
for (socketinfo* si = svc->sockets; si; si = si->next) {
char tmp[128];
snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
unlink(tmp);
}
//當flags為EXEC時,釋放相應的服務
if (svc->flags & SVC_EXEC) {
waiting_for_exec = false;
list_remove(&svc->slist);
free(svc->name);
free(svc);
return true;
}
svc->pid = 0;
svc->flags &= (~SVC_RUNNING);
//對於ONESHOT服務,使其進入disabled狀態
if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
//禁用和重置的服務,都不再自動重啟
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped"); //設定相應的service狀態為stopped
return true;
}
//服務在4分鐘內重啟次數超過4次,則重啟手機進入recovery模式
time_t now = gettime();
if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
return true;
}
} else {
svc->time_crashed = now;
svc->nr_crashed = 1;
}
}
svc->flags &= (~SVC_RESTART);
svc->flags |= SVC_RESTARTING;
//執行當前service中所有onrestart命令
struct listnode* node;
list_for_each(node, &svc->onrestart.commands) {
command* cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
//設定相應的service狀態為restarting
svc->NotifyStateChange("restarting");
return true;
}
另外:通過getprop | grep init.svc
可檢視所有的service執行狀態。狀態總共分為:running, stopped, restarting
2.3 register_epoll_handler
[-> signal_handler.cpp]
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN; //可讀
ev.data.ptr = reinterpret_cast<void*>(fn);
//將fd的可讀事件加入到epoll_fd的監聽佇列中
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
ERROR("epoll_ctl failed: %s\n", strerror(errno));
}
}
三、rc檔案語法
rc檔案語法是以行尾單位,以空格間隔的語法,以#開始代表註釋行。rc檔案主要包含Action、Service、Command、Options,其中對於Action和Service的名稱都是唯一的,對於重複的命名視為無效。
3.1 Action
Action: 通過trigger,即以 on開頭的語句,決定何時執行相應的service。
- on early-init; 在初始化早期階段觸發;
- on init; 在初始化階段觸發;
- on late-init; 在初始化晚期階段觸發;
- on boot/charger: 當系統啟動/充電時觸發,還包含其他情況,此處不一一列舉;
- on property:<key>=<value>: 當屬性值滿足條件時觸發;
3.2 Service
服務Service,以 service開頭,由init程序啟動,一般運行於另外一個init的子程序,所以啟動service前需要判斷對應的可執行檔案是否存在。init生成的子程序,定義在rc檔案,其中每一個service,在啟動時會通過fork方式生成子程序。
例如: service servicemanager /system/bin/servicemanager
代表的是服務名為servicemanager,服務的路徑,也就是服務執行操作時執行/system/bin/servicemanager。
3.3 Command
下面列舉常用的命令
- class_start <service_class_name>: 啟動屬於同一個class的所有服務;
- start <service_name>: 啟動指定的服務,若已啟動則跳過;
- stop <service_name>: 停止正在執行的服務
- setprop <name> <value>:設定屬性值
- mkdir <path>:建立指定目錄
- symlink <target> <sym_link>: 建立連線到<target>的<sym_link>符號連結;
- write <path> <string>: 向檔案path中寫入字串;
- exec: fork並執行,會阻塞init程序直到程式完畢;
- exprot <name> <name>:設定環境變數;
- loglevel <level>:設定log級別
3.4 Options
Options是Services的可選項,與service配合使用
- disabled: 不隨class自動啟動,只有根據service名才啟動;
- oneshot: service退出後不再重啟;
- user/group: 設定執行服務的使用者/使用者組,預設都是root;
- class:設定所屬的類名,當所屬類啟動/退出時,服務也啟動/停止,預設為default;
- onrestart:當服務重啟時執行相應命令;
- socket: 建立名為
/dev/socket/<name>
的socket - critical: 在規定時間內該service不斷重啟,則系統會重啟並進入恢復模式
default: 意味著disabled=false,oneshot=false,critical=false。
四、啟動服務
4.1 啟動順序
on early-init
on init
on late-init //掛載檔案系統,啟動核心服務
trigger post-fs
trigger load_system_props_action
trigger post-fs-data //掛載data
trigger load_persist_props_action
trigger firmware_mounts_complete
trigger boot
on post-fs
start logd
mount rootfs rootfs / ro remount
mount rootfs rootfs / shared rec
mount none /mnt/runtime/default /storage slave bind rec
...
on post-fs-data
start logd
start vold //啟動vold
...
on boot
...
class_start core //啟動core class
由on early-init -> init -> late-init -> boot。
先啟動core class, 至於main class的啟動是由vold.decrypt的以下4個值的設定所決定的, 該過程位於system/vold/cryptfs.c檔案。
on nonencrypted
class_start main
class_start late_start
on property:vold.decrypt=trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
class_start main
class_start late_start
on property:vold.decrypt=trigger_reset_main
class_reset main
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
4.2 服務啟動(Zygote)
在init.zygote.rc檔案中,zygote服務定義如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
通過init_parser.cpp
完成整個service解析工作,此處就不詳細展開講解析過程,該過程主要是建立一個名”zygote”的service結構體,一個socketinfo結構體(用於socket通訊),以及一個包含4個onrestart的action結構體。
Zygote服務會隨著main class的啟動而啟動,退出後會由init重啟zygote,即使多次重啟也不會進入recovery模式。zygote所對應的可執行檔案是/system/bin/app_process,通過呼叫pid =fork()
建立子程序,通過execve(svc->args[0], (char**)svc->args, (char**) ENV)
,進入App_main.cpp的main()函式。故zygote是通過fork和execv共同建立的。
流程如下:
而關於Zygote重啟在前面的訊號處理過程中講過,是處理SIGCHLD訊號,init程序重啟zygote程序,更多關於Zygote內容見Zygote篇。
4.3 服務重啟
當init子程序退出時,會產生SIGCHLD訊號,併發送給init程序,通過socket套接字傳遞資料,呼叫到wait_for_one_process()方法,根據是否是oneshot,來決定是重啟子程序,還是放棄啟動。
所有的Service裡面只有servicemanager ,zygote ,surfaceflinger這3個服務有onrestart
關鍵字來觸發其他service啟動過程。
//zygote可觸發media、netd重啟
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
//servicemanager可觸發healthd、zygote、media、surfaceflinger、drm重啟
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
//surfaceflinger可觸發zygote重啟
service surfaceflinger /system/bin/surfaceflinger
class core
user system
group graphics drmrpc
onrestart restart zygote
由上可知:
- zygote:觸發media、netd以及子程序(包括system_server程序)重啟;
- system_server: 觸發zygote重啟;
- surfaceflinger:觸發zygote重啟;
- servicemanager: 觸發zygote、healthd、media、surfaceflinger、drm重啟
所以,surfaceflinger,servicemanager,zygote自身以及system_server程序被殺都會觸發Zygote重啟。
五、屬性服務
當某個程序A,通過property_set()修改屬性值後,init程序會檢查訪問許可權,當權限滿足要求後,則更改相應的屬性值,屬性值一旦改變則會觸發相應的觸發器(即rc檔案中的on開頭的語句),在Android Shared Memmory(共享記憶體區域)中有一個_system_property_area_區域,裡面記錄著所有的屬性值。對於程序A通過property_get()方法,獲取的也是該共享記憶體區域的屬性值。
5.1 初始化
[-> property_service.cpp]
void property_init() {
//用於保證只初始化_system_property_area_區域一次
if (property_area_initialized) {
return;
}
property_area_initialized = true;
if (__system_property_area_init()) {
return;
}
pa_workspace.size = 0;
pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (pa_workspace.fd == -1) {
ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
return;
}
}
在properyty_init函式中,先呼叫init_property_area函式,建立一塊用於儲存屬性的共享記憶體,而共享記憶體是可以跨程序的。
關於載入的prop檔案
通過load_all_load_all_propsprops()
方法,載入以下:
- /system/build.prop;
- /vendor/build.prop;
- /factory/factory.prop;
- /data/local.prop;
- /data/property路徑下的persist屬性
5.2 屬性類別
- 屬性名以
ctl.
開頭,則表示是控制訊息,控制訊息用來執行一些命令。例如:- setprop ctl.start bootanim 檢視開機動畫;
- setprop ctl.stop bootanim 關閉開機動畫;
- setprop ctl.start pre-recovery 進入recovery模式。
- 屬性名以
ro.
開頭,則表示是隻讀的,不能設定,所以直接返回。 - 屬性名以
persist.
開頭,則需要把這些值寫到對應檔案中去。
以上內容轉自:gityuan的Android系統啟動-Init篇
-----------------------------------------------------------分割線-------------------------------------------------------------------------------
以下內容為個人小結:
serviceManager、surfaceflinger、zygote本身都會導致zygote的重啟,而zygote重啟會導致system_server重啟,而system_server的重啟,system_server在重啟前肯定會先把屬於它管理的應用程序都殺死。service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server這個service宣告就表明zygote的引數是--start-system-server,所以會去重啟system_server