基於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