Android init第三、四部分详细分析
本文一定要在詳細閱讀了,系列的第二篇文章時候,再來閱讀。
/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(keychord_init_action, "keychord_init");queue_builtin_action(console_init_action, "console_init");/* execute all the boot actions to get us started */action_for_each_trigger("init", action_add_queue_tail);/* skip mounting filesystems in charger mode */if (!is_charger) {action_for_each_trigger("early-fs", action_add_queue_tail);action_for_each_trigger("fs", action_add_queue_tail);action_for_each_trigger("post-fs", action_add_queue_tail);action_for_each_trigger("post-fs-data", action_add_queue_tail);}queue_builtin_action(property_service_init_action, "property_service_init");queue_builtin_action(signal_init_action, "signal_init");queue_builtin_action(check_startup_action, "check_startup");if (is_charger) {action_for_each_trigger("charger", action_add_queue_tail);} else {action_for_each_trigger("early-boot", action_add_queue_tail);action_for_each_trigger("boot", action_add_queue_tail);}/* run all property triggers based on current state of the properties */queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");以上就是/init的第三部分,簡單說就是把在/init.rc中解析到的Action,通過trigger掛載到全局變量action_queue中。action_queue其實就是一個等待被執行的Action隊列。
那么我們來分析用到的函數action_for_each_trigger和queue_builtin_action
函數action_for_each_trigger(/system/core/init/init_parser.c)
void action_for_each_trigger(const char *trigger,void (*func)(struct action *act)) {struct listnode *node;struct action *act;list_for_each(node, &action_list) {act = node_to_item(node, struct action, alist);if (!strcmp(act->name, trigger)) {func(act);}} }我們通過上一篇文章的分析知道,Action的name就是Trigger的名字。由于要分析不只一個文件,所以不同的Action可以有相同的名字也就是相同Trigger^trigger。
例如/init.rc和/init.trace.rc都有boot這個Trigger。
多個action_for_each_trigger,只用過一種func參數就是action_add_queue_tail:
void action_add_queue_tail(struct action *act) {list_add_tail(&action_queue, &act->qlist); }函數很簡單,就是把指定的Action掛到action_queue的尾部。注意,此時該Action還在action_list中的。
函數queue_builtin_action(/system/core/init/init_parser.c)
void queue_builtin_action(int (*func)(int nargs, char **args), char *name) {struct action *act;struct command *cmd;act = calloc(1, sizeof(*act));act->name = name;list_init(&act->commands);cmd = calloc(1, sizeof(*cmd));cmd->func = func;cmd->args[0] = name;list_add_tail(&act->commands, &cmd->clist);list_add_tail(&action_list, &act->alist);action_add_queue_tail(act); }該函數就是把一些builtin action(這些action不在rc文件中,是系統內置的),同時掛載到action_list和action_queue中。這些Action的Command只有一個就是傳入的第一個參數。 系統的builtin action
wait_for_coldboot_done_action系統執行的第一個服務是ueventd用于檢測并掛載設備。有些設備在內核加載時同時加載了,所以需要內核重新發送uevent事件向ueventd服務注冊,稱為coldboot。該Action通過wait_for_file函數等待coldboot完成(coldboot完成是ueventd會創健coldboot_done文件)。
keychord_init_action初始化keychord,一些服務可以通過keychord開啟
console_init_action查看/dev/console是否能讀寫,并調用load_565rle_image加載開機動畫。
property_service_init_action啟動Property Service在第一部分的初始化中完成的只是在/init中的set和get。通過建立一個socket并監聽來實現在全系統中的set和get功能。(主要調用/system/core/init/property_service.c中的start_property_service函數)
signal_init_action建立新的signal handler處理SIGCHLD信號,目的是在各個服務退出后進行相應的操作(因為服務都是/init的子進程所以是SIGCHLD)。
check_startup_action檢查上面兩個Action是否成功。
queue_property_triggers_action有些rc文件中的Action是以on property:<name>=<value>形式的。本Action的作用就是通過讀取Property System檢查這樣的Action是否滿足,是的話掛載到action_queue尾部。?/init程序第四部分
for(;;) {int nr, i, timeout = -1;execute_one_command();restart_processes();if (!property_set_fd_init && get_property_set_fd() > 0) {ufds[fd_count].fd = get_property_set_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;property_set_fd_init = 1;}if (!signal_fd_init && get_signal_fd() > 0) {ufds[fd_count].fd = get_signal_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;signal_fd_init = 1;}if (!keychord_fd_init && get_keychord_fd() > 0) {ufds[fd_count].fd = get_keychord_fd();ufds[fd_count].events = POLLIN;ufds[fd_count].revents = 0;fd_count++;keychord_fd_init = 1;}if (process_needs_restart) {timeout = (process_needs_restart - gettime()) * 1000;if (timeout < 0)timeout = 0;}if (!action_queue_empty() || cur_action)timeout = 0;#if BOOTCHARTif (bootchart_count > 0) {if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)timeout = BOOTCHART_POLLING_MS;if (bootchart_step() < 0 || --bootchart_count == 0) {bootchart_finish();bootchart_count = 0;}} #endifnr = poll(ufds, fd_count, timeout);if (nr <= 0)continue;for (i = 0; i < fd_count; i++) {if (ufds[i].revents == POLLIN) {if (ufds[i].fd == get_property_set_fd())handle_property_set_fd();else if (ufds[i].fd == get_keychord_fd())handle_keychord();else if (ufds[i].fd == get_signal_fd())handle_signal();}}}函數execute_one_command
void execute_one_command(void) {int ret;if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {cur_action = action_remove_queue_head();cur_command = NULL;if (!cur_action)return;INFO("processing action %p (%s)\n", cur_action, cur_action->name);cur_command = get_first_command(cur_action);} else {cur_command = get_next_command(cur_action, cur_command);}if (!cur_command)return;ret = cur_command->func(cur_command->nargs, cur_command->args);INFO("command '%s' r=%d\n", cur_command->args[0], ret); }該函數就是不斷執行action_queue中Action的Command。
函數restart_processes
static void restart_processes() {process_needs_restart = 0;service_for_each_flags(SVC_RESTARTING,restart_service_if_needed); }函數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;} }該函數比較難理解,其實是執行了如下的功能:
- 服務的重啟間隔是5秒,如果需要重啟的服務啟動超過5秒就需要重啟。
- 把全局變量process_need_restart的值設置成所有需要重啟的服務的最近的重啟時間。
在Android系統中由于文件在fork是繼承的,往往/init和子進程——服務之間的通訊可以通過操作文件實現:
某個服務向要向`/init`通訊,只要操作相應的繼承過來的的文件。而`/init`通過`poll`輪詢這些文件。一旦文件被操作時,`/init`就可以知道,從而調用相應的處理函數。/init中有三個這樣重要的文件:
property_set_fd用于監聽property set事件
signal_fd用于監聽服務的退出事件
keychord_fd用于監聽keychord事件
把這三個文件描述符放到ufds中,通過poll^poll函數輪詢對這三個文件的操作事件,分別執行
handle_property_set_fd();處理property set事件
handle_keychord();處理keychord事件
handle_signal();處理服務子信號(SIGCHLD)事件
我們只重點分析一下handle_signal函數:
void handle_signal(void) {char tmp[32];/* we got a SIGCHLD - reap and restart as needed */read(signal_recv_fd, tmp, sizeof(tmp));while (!wait_for_one_process(0)); }該函數調用wait_for_process
static int wait_for_one_process(int block) {pid_t pid;int status;struct service *svc;struct socketinfo *si;time_t now;struct listnode *node;struct command *cmd;while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );if (pid <= 0) return -1;INFO("waitpid returned pid %d, status = %08x\n", pid, status);svc = service_find_by_pid(pid);if (!svc) {ERROR("untracked pid %d exited\n", pid);return 0;}NOTICE("process '%s', pid %d exited\n", svc->name, pid);if (!(svc->flags & SVC_ONESHOT)) {kill(-pid, SIGKILL);NOTICE("process '%s' killing any children in process group\n", svc->name);}/* remove any sockets we may have created */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);/* oneshot processes go into the disabled state on exit */if (svc->flags & SVC_ONESHOT) {svc->flags |= SVC_DISABLED;}/* disabled and reset processes do not get restarted automatically */if (svc->flags & (SVC_DISABLED | SVC_RESET) ) {notify_service_state(svc->name, "stopped");return 0;}now = gettime();if (svc->flags & SVC_CRITICAL) {if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {ERROR("critical process '%s' exited %d times in %d minutes; ""rebooting into recovery mode\n", svc->name,CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);android_reboot(ANDROID_RB_RESTART2, 0, "recovery");return 0;}} else {svc->time_crashed = now;svc->nr_crashed = 1;}}svc->flags |= SVC_RESTARTING;/* Execute all onrestart commands for this service. */list_for_each(node, &svc->onrestart.commands) {cmd = node_to_item(node, struct command, clist);cmd->func(cmd->nargs, cmd->args);}notify_service_state(svc->name, "restarting");return 0; }該函數功能如下:
-
通過waitpid函數獲取退出的子服務的pid。如果服務是onshot的直接kill掉,否則就要標記成重啟狀態。
svc->flags |= SVC_RESTARTING;
-
關閉該服務的socket
-
如果是critical的服務在一定時間內退出超過4次,就調用reboot_android進入recovery mode。
-
在服務重啟之前執行Onrestart上的命令。
值得注意的是服務沒有在此函數中真正重啟,只是設置成SVC_RESTARTING。
真正的重啟是在上面分析的restart_processes中通過調用service_start完成。
總結
以上是生活随笔為你收集整理的Android init第三、四部分详细分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ElasticSearch模糊查询(中文
- 下一篇: mysql查询自定义数据_实现自定义查询