2. 第二個開機畫面的顯示過程sql
- int main(int argc, char **argv)
- {
- int fd_count = 0;
- struct pollfd ufds[4];
- ......
- int property_set_fd_init = 0;
- int signal_fd_init = 0;
- int keychord_fd_init = 0;
- if (!strcmp(basename(argv[0]), "ueventd"))
- return ueventd_main(argc, argv);
- ......
- queue_builtin_action(console_init_action, "console_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;
- ......
- nr = 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();
- }
- }
- }
- return 0;
- }
接下來調用另一個函數queue_builtin_action來向init進程中的一個待執行action隊列增長了一個名稱等於「console_init」的action。這個action對應的執行函數爲console_init_action,它就是用來顯示第二個開機畫面的。socket
函數queue_builtin_action實如今文件system/core/init/init_parser.c文件中,以下所示:ide
- static list_declare(action_list);
- static list_declare(action_queue);
- 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);
- }
- void action_add_queue_tail(struct action *act)
- {
- list_add_tail(&action_queue, &act->qlist);
- }
action_list列表用來保存從啓動腳本/init.rc解析獲得的一系列action,以及一系列內建的action。當這些action須要執行的時候,它們就會被添加到action_queue列表中去,以便init進程能夠執行它們。函數
回到init進程的入口函數main中,最後init進程會進入到一個無限循環中去。在這個無限循環中,init進程會作如下五個事情:
ui
A. 調用函數execute_one_command來檢查action_queue列表是否爲空。若是不爲空的話,那麼init進程就會將保存在列表頭中的action移除,而且執行這個被移除的action。因爲前面咱們將一個名稱爲「console_init」的action添加到了action_queue列表中,所以,在這個無限循環中,這個action就會被執行,即函數console_init_action會被調用。spa
B. 調用函數restart_processes來檢查系統中是否有進程須要重啓。在啓動腳本/init.rc中,咱們能夠指定一個進程在退出以後會自動從新啓動。在這種狀況下,函數restart_processes就會檢查是否存在須要從新啓動的進程,若是存在的話,那麼就會將它從新啓動起來。rest
C. 處理系統屬性變化事件。當咱們調用函數property_set來改變一個系統屬性值時,系統就會經過一個socket(經過調用函數get_property_set_fd能夠得到它的文件描述符)來向init進程發送一個屬性值改變事件通知。init進程接收到這個屬性值改變事件以後,就會調用函數handle_property_set_fd來進行相應的處理。後面在分析第三個開機畫面的顯示過程時,咱們就會看到,SurfaceFlinger服務就是經過修改「ctl.start」和「ctl.stop」屬性值來啓動和中止第三個開機畫面的。blog
D. 處理一種稱爲「chorded keyboard」的鍵盤輸入事件。這種類型爲chorded keyboard」的鍵盤設備經過不一樣的銨鍵組合來描述不一樣的命令或者操做,它對應的設備文件爲/dev/keychord。咱們能夠經過調用函數get_keychord_fd來得到這個設備的文件描述符,以即可以監控它的輸入事件,而且調用函數handle_keychord來對這些輸入事件進行處理。接口
E. 回收殭屍進程。咱們知道,在Linux內核中,若是父進程不等待子進程結束就退出,那麼當子進程結束的時候,就會變成一個殭屍進程,從而佔用系統的資源。爲了回收這些殭屍進程,init進程會安裝一個SIGCHLD信號接收器。當那些父進程已經退出了的子進程退出的時候,內核就會發出一個SIGCHLD信號給init進程。init進程能夠經過一個socket(經過調用函數get_signal_fd能夠得到它的文件描述符)來將接收到的SIGCHLD信號讀取回來,而且調用函數handle_signal來對接收到的SIGCHLD信號進行處理,即回收那些已經變成了殭屍的子進程。隊列
注意,因爲後面三個事件都是能夠經過文件描述符來描述的,所以,init進程的入口函數main使用poll機制來同時輪詢它們,以即可以提升效率。