android啟動--深入理解init進程

jopen 12年前發布 | 23K 次閱讀 Android Android開發 移動開發

init是一個進程,它是linux系統中用戶空間的第一個進程,其進程PID是1,父進程為linux
系統內核的0號進程。所以其被賦予很多極其重要的職責,linux內核初始化完成后就開始執行它。


代碼路徑:\system\core\init\init.c


下面就分析一下先吧,只分析重點的函數功能:
int main(int argc, char **argv)
{
   // 1、設置子進程退出的信號處理函數:sigchld_handler
     act.sa_handler = sigchld_handler;
     act.sa_flags = SA_NOCLDSTOP;
     act.sa_mask = 0;
     act.sa_restorer = NULL;
     sigaction(SIGCHLD, &act, 0);

     // 2、創建文件夾,并掛載設備
     mkdir("/dev", 0755);
     mkdir("/proc", 0755);
    ....
    
     // 3、重定向標準輸入輸出錯誤到 /dev/null
     open_devnull_stdio();
    
     // 4、解析 init.rc 腳本
     parse_config_file("/init.rc");


// 5、解析機器相關的配置文件,一般相關的放在init.rc中利用service action調過去
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
     parse_config_file(tmp);


// 6、建立 uevent,用于與linux kernel交互的socket
device_fd = device_init();

// 7、初始化及加載屬性相關資源,這里利用ashmem共享,屬于整個系統的資源
property_init();

// 8、執行 on init 、early-boot 及 boot 片段動作,這些定義于 init.rc 中
     / execute all the boot actions to get us started /
     action_for_each_trigger("init", action_add_queue_tail);
     drain_action_queue();
    
     / execute all the boot actions to get us started /
     action_for_each_trigger("early-boot", action_add_queue_tail);
     action_for_each_trigger("boot", action_add_queue_tail);

   // 9、這里定義的init進程需要關注的四個方面事情  
ufds[0].fd = device_fd;
     ufds[0].events = POLLIN;   // Uevent事件
     ufds[1].fd = property_set_fd;
     ufds[1].events = POLLIN;   // 屬性事件
     ufds[2].fd = signal_recv_fd;
     ufds[2].events = POLLIN;   // 子進程事件
     fd_count = 3;


   ufds[3].fd = keychord_fd;
     ufds[3].events = POLLIN;  // keychord熱鍵事件
     fd_count++;

// ok, 下面就是init進程的處理循環
for(;;) {
// I、執行init.rc 腳本中的動作
         drain_action_queue();
        
         // II、執行標志為SVC_RESTARTING的進程,利用fork+execve啟動新的進程
         restart_processes();


// III、調用poll輪詢上述的四個事件
         nr = poll(ufds, fd_count, timeout);
         if (nr <= 0)
             continue;


// IV、依次處理各個事件
         if (ufds[2].revents == POLLIN) {
             / we got a SIGCHLD - reap and restart as needed /
             read(signal_recv_fd, tmp, sizeof(tmp));
             while (!wait_for_one_process(0))
                 ;
             continue;
        }


         if (ufds[0].revents == POLLIN)
             handle_device_fd(device_fd);


         if (ufds[1].revents == POLLIN)
             handle_property_set_fd(property_set_fd);
         if (ufds[3].revents == POLLIN)
             handle_keychord(keychord_fd);
...
}

return 0;
}


這里我們重點關注一下service的解析過程:


這里以啟動 zygote 為例講解一下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666   //創建一個stream流式套接字
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media


服務名稱: zygote
進程路徑: /system/bin/app_process
進程啟動參數:-Xzygote /system/bin --zygote --start-system-server

解析入口函數:parse_new_section

void parse_new_section(struct parse_state state, int kw,
                       int nargs, char **args)
{
    switch(kw) {
    case K_service:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
...
}


--> 其中所有的解析的service放在如下雙鏈表結構中
struct service {
    /
list of all services /
    struct listnode slist;


    const char
name;
    const char classname; //所屬class名字,默認為"default"


    unsigned flags;
    pid_t pid;


    struct socketinfo
sockets;
    struct svcenvinfo envvars;


    struct action onrestart;  /
Actions to execute on restart. /
    ...
};


static void
parse_service(struct parse_state state, int nargs, char **args)
{
    struct service
svc;

    svc = service_find_by_name(args[1]);

    svc->name = args[1];
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char) nargs);
    svc->args[nargs] = 0;
    svc->nargs = nargs;
    svc->onrestart.name = "onrestart";
    list_init(&svc->onrestart.commands);


    // 加入到全局 service_list 鏈表中
    list_add_tail(&service_list, &svc->slist);
    return svc;
}


然后執行點都使用 list_for_each(node, &service_list) 循環遍歷所有服務及執行動作


service 啟動:


在解析 init.rc 腳本中 
class_start default 
這個class_start 是一個COMMAND,其對應的函數是 do_class_start ,注意類似的COMMAND也一樣
加上前綴 do_xxx 即函數名稱: ex: mount 對應于 do_mount 
代碼:/system/core/init/builtins.c


do_class_start()
-->service_start_if_not_disabled()
--> service_start(svc, NULL);


void service_start(struct service svc, const char dynamic_args)
{
    // 已經運行則直接返回
    if (svc->flags & SVC_RUNNING) {
        return;
    }
    
    NOTICE("starting '%s'\n", svc->name);
    pid = fork();  //創建子進程 
    if(pid == 0){
     // 運行于子進程中
    
     // 準備好執行環境
     add_environment(ei->name, ei->value);
    create_socket(si->name,
                         !strcmp(si->type, "dgram") ? 
                         SOCK_DGRAM : SOCK_STREAM,
                         si->perm, si->uid, si->gid);    
    
     // 開始執行具體的進程
     execve(svc->args[0], (char) arg_ptrs, (char) ENV);
    }


    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;


    notify_service_state(svc->name, "running");
}


onrestart 用于重啟使用,這就是最開始那個 sigchld_handler 函數:


static void sigchld_handler(int s)
{
    write(signal_fd, &s, 1); // 寫數據
}

讀取數據處理:
在 init 進程的main函數中處理:
    if (ufds[2].revents == POLLIN) {
        / we got a SIGCHLD - reap and restart as needed /
        read(signal_recv_fd, tmp, sizeof(tmp));
        while (!wait_for_one_process(0))
            ;
        continue;
    }

-->
static int wait_for_one_process(int block)
{
    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );


// 如果zygote 死則整個系統崩潰
    if (!(svc->flags & SVC_ONESHOT)) {
        kill(-pid, SIGKILL);
        NOTICE("process '%s' killing any children in process group\n", svc->name);
    }

//超時及重啟次數系統重啟進入recovery模式
    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);
                sync();
                __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                         LINUX_REBOOT_CMD_RESTART2, "recovery");
                return 0;
            }
        }
...
</span>

}

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!