Android Init進程分析

之前在看android啓動過程總是帶着完成工作任務的目的去分析代碼,但是對於一些代碼的細節並不是很清楚,在這裏就分析一下Init進程的執行過程。

以下框圖簡要描述系統進程間層次關係


Init進程是android系統起來之後啓動的第一個進程,對於研究android系統的啓動過程很重要。它是android系統中的始祖進程,USB守護進程(usbd),掛載守護進程(vold),無線接口守護進程(rild)等都是init進程的子進程。以下截圖是我手機的運行進程情況,可以明顯看出進程間的關係

還有一個PID=2的kthread進程,該進程用來創建內核空間的其它進程




直接根據代碼來分析整個進程的執行過程。

int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];//存放pollfd
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    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);//ueventd是init的軟鏈接,執行這個進程的時候相當於執行init進程,然後根據進程名進入相應的執行流程

    /* clear the umask */
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    mkdir("/dev", 0755);//創建一些必要的目錄並分配權限
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */

以上主要創建一些文件系統目錄並掛載相應的文件系統,proc文件系統是重要的內核數據的接口,可以通過它讀取一些系統信息還能操作內核參數

    open_devnull_stdio();//重定向標準輸入,輸入,錯誤到/dev/__null__(dup2複製文件句柄,0,1,2分別代表標準輸入 輸出 錯誤) 屏蔽標準輸入輸出
    log_init();//設置log信息輸出設備/dev/__kmsg__,unlink之後其他進程無法訪問,閱讀源碼定向到printk函數輸出 初始化log系統
  
    property_init();//初始化屬性系統,這個可以以後分析


    get_hardware_name(hardware, &revision);


    process_kernel_cmdline();


#ifdef HAVE_SELINUX
    INFO("loading selinux policy\n");
    selinux_load_policy();
#endif


    is_charger = !strcmp(bootmode, "charger");


    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();

這裏導入相應的處理函數,分析執行過程

static void import_kernel_cmdline(int in_qemu)
{
    char cmdline[1024];
    char *ptr;
    int fd;

    fd = open("/proc/cmdline", O_RDONLY);
    if (fd >= 0) {
        int n = read(fd, cmdline, 1023);
        if (n < 0) n = 0;

        /* get rid of trailing newline, it happens */
        if (n > 0 && cmdline[n-1] == '\n') n--;
	//讀取/proc/cmdline中的信息,存放在cmdline字符數組並進行處理
        cmdline[n] = 0;
        close(fd);
    } else {
        cmdline[0] = 0;
    }

    ptr = cmdline;
    while (ptr && *ptr) {
        char *x = strchr(ptr, ' ');
        if (x != 0) *x++ = 0;
        import_kernel_nv(ptr, in_qemu);//根據' '間斷符逐行分析文本
        ptr = x;
    }

        /* don't expose the raw commandline to nonpriv processes */
    chmod("/proc/cmdline", 0440);
}

static void import_kernel_nv(char *name, int in_qemu)
{
    char *value = strchr(name, '=');

    if (value == 0) {
	if (!strcmp(name, "calibration"))
	    calibration = 1;//表示要校準還是什麼?
	return;
    }
    *value++ = 0;
    if (*name == 0) return;

    if (!in_qemu)
    {
        /* on a real device, white-list the kernel options */
        if (!strcmp(name,"qemu")) {
            strlcpy(qemu, value, sizeof(qemu));
        } else if (!strcmp(name,"androidboot.console")) {
            strlcpy(console, value, sizeof(console));
        } else if (!strcmp(name,"androidboot.mode")) {
            strlcpy(bootmode, value, sizeof(bootmode));//啓動模式
        } else if (!strcmp(name,"androidboot.serialno")) {
            strlcpy(serialno, value, sizeof(serialno));
        } else if (!strcmp(name,"androidboot.baseband")) {
            strlcpy(baseband, value, sizeof(baseband));//基帶
        } else if (!strcmp(name,"androidboot.carrier")) {
            strlcpy(carrier, value, sizeof(carrier));
        } else if (!strcmp(name,"androidboot.bootloader")) {
            strlcpy(bootloader, value, sizeof(bootloader));
        } else if (!strcmp(name,"androidboot.hardware")) {
            strlcpy(hardware, value, sizeof(hardware));
        }//將以上設備信息存放在定義的字符數組中
    } else {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char  buff[32];
        int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
        if (len < (int)sizeof(buff)) {
            property_set( buff, value );
        }
    }
}


    get_hardware_name(hardware, &revision);
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    init_parse_config_file(tmp);//分析相應硬件版本的rc文件

init.rc文件有自己相應的語法,分析rc文件也是根據對應的語法來分析,這裏引入一片簡單介紹init.rc語法的文章

Android init.rc腳本解析

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);//這裏通過read_file函數將fn文件中的數據全部讀取到data緩衝區中,malloc分配空間
    if (!data) return -1;
    //這裏開始真正分析腳本中的命令
    parse_config(fn, data);
    DUMP();
    return 0;
}

解析過程會先將init.rc文件action與service進行解析,然後插入到鏈表中依次執行,查看源碼中對鏈表的定義

#ifndef _CUTILS_LIST_H_
#define _CUTILS_LIST_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
//聲明一個雙向鏈表
struct listnode
{
    struct listnode *next;
    struct listnode *prev;
};
//計算結構體數據變量相對於結構體首地址的偏移量,這個很重要
#define node_to_item(node, container, member) \
    (container *) (((char*) (node)) - offsetof(container, member))
//聲明一個雙向鏈表,並且指向自己
#define list_declare(name) \
    struct listnode name = { \
        .next = &name, \
        .prev = &name, \
    }
//遍歷鏈表
#define list_for_each(node, list) \
    for (node = (list)->next; node != (list); node = node->next)
//反向遍歷鏈表
#define list_for_each_reverse(node, list) \
    for (node = (list)->prev; node != (list); node = node->prev)

void list_init(struct listnode *list);//初始化一個雙向鏈表
void list_add_tail(struct listnode *list, struct listnode *item);//將結點添加至雙向鏈表尾部
void list_remove(struct listnode *item);

#define list_empty(list) ((list) == (list)->next)
#define list_head(list) ((list)->next)
#define list_tail(list) ((list)->prev)

#ifdef __cplusplus
};
#endif /* __cplusplus */

#endif

這裏聲明瞭三個鏈表

static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);


static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    char *args[INIT_PARSER_MAXARGS];//允許解析出來的命令行最多有64個參數
    int nargs;

    nargs = 0;
    state.filename = fn;//文件名
    state.line = 0;
    state.ptr = s;//data
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;//此時解析函數是空操作
    for (;;) {
        switch (next_token(&state)) {//通過next_token函數來尋找字符數組中的關鍵標記
        //這裏面省略了一些字符的處理(如‘\r’, '\t', '"', ' '等),只針對有效字符進行處理('\0', '\n'等)
        //#define T_EOF 0    #define T_TEXT 1    #define T_NEWLINE 2
        case T_EOF:
            state.parse_line(&state, 0, 0);                                                                                                     goto parser_done;
            return;
        case T_NEWLINE:
            if (nargs) {
                int kw = lookup_keyword(args[0]);//這裏將分析第一個參數所代表的關鍵字
                //根據字符匹配返回已定義好的宏定義
                if (kw_is(kw, SECTION)) {//當關鍵字是on或service或import
                    state.parse_line(&state, 0, 0); //此時相當於什麼都沒做
                    parse_new_section(&state, kw, nargs, args);//對state.parse_line進行填充
                } else {
                    state.parse_line(&state, nargs, args);//對於NEWLINE不是on service import的調用parse_line,而在後面的填充中                                                                     //parse_line函數即parse_line_action

                    //回調相應的處理函數
                }
                nargs = 0;
            }
            break;
        case T_TEXT://不處理
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }                                                                                                                                   parser_done:
    list_for_each(node, &import_list) {//文件解析結束後解析新導入的rc文件
         struct import *import = node_to_item(node, struct import, list);
         int ret;
      
         //循環取出rc文件的路徑   
         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);//重新解析rc文件
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}

首先查看一下keywords.h這個文件,對分析過程有幫助

#ifndef KEYWORD//防止重複定義
int do_chroot(int nargs, char **args);
int do_chdir(int nargs, char **args);
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
int do_class_reset(int nargs, char **args);
int do_domainname(int nargs, char **args);
int do_exec(int nargs, char **args);
int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount_all(int nargs, char **args);
int do_mount(int nargs, char **args);
int do_restart(int nargs, char **args);
int do_restorecon(int nargs, char **args);
int do_rm(int nargs, char **args);
int do_rmdir(int nargs, char **args);
int do_setcon(int nargs, char **args);
int do_setenforce(int nargs, char **args);
int do_setkey(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_setsebool(int nargs, char **args);
int do_start(int nargs, char **args);
int do_stop(int nargs, char **args);
int do_trigger(int nargs, char **args);
int do_symlink(int nargs, char **args);
int do_sysclktz(int nargs, char **args);
int do_write(int nargs, char **args);
int do_copy(int nargs, char **args);
int do_chown(int nargs, char **args);
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_load_persist_props(int nargs, char **args);
int do_wait(int nargs, char **args);
int do_ubiattach(int argc, char **args);
int do_ubidetach(int argc, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,//#與##是宏定義的連接符
enum {
    K_UNKNOWN,
#endif
    KEYWORD(capability,  OPTION,  0, 0)//這裏返回的相當於K_capabikity
    KEYWORD(chdir,       COMMAND, 1, do_chdir)//K_chdir
    KEYWORD(chroot,      COMMAND, 1, do_chroot)//K_chroot
    KEYWORD(class,       OPTION,  0, 0)
    KEYWORD(class_start, COMMAND, 1, do_class_start)
    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
    KEYWORD(console,     OPTION,  0, 0)
    KEYWORD(critical,    OPTION,  0, 0)
    KEYWORD(dalvik_recache,    OPTION,  0, 0)
    KEYWORD(disabled,    OPTION,  0, 0)
    KEYWORD(domainname,  COMMAND, 1, do_domainname)
    KEYWORD(exec,        COMMAND, 1, do_exec)
    KEYWORD(export,      COMMAND, 2, do_export)
    KEYWORD(group,       OPTION,  0, 0)
    KEYWORD(hostname,    COMMAND, 1, do_hostname)
    KEYWORD(ifup,        COMMAND, 1, do_ifup)
    KEYWORD(insmod,      COMMAND, 1, do_insmod)
    KEYWORD(import,      SECTION, 1, 0)
    KEYWORD(keycodes,    OPTION,  0, 0)
    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
    KEYWORD(mount,       COMMAND, 3, do_mount)
    KEYWORD(on,          SECTION, 0, 0)
    KEYWORD(oneshot,     OPTION,  0, 0)
    KEYWORD(onrestart,   OPTION,  0, 0)
    KEYWORD(restart,     COMMAND, 1, do_restart)
    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
    KEYWORD(rm,          COMMAND, 1, do_rm)
    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
    KEYWORD(seclabel,    OPTION,  0, 0)
    KEYWORD(service,     SECTION, 0, 0)
    KEYWORD(setcon,      COMMAND, 1, do_setcon)
    KEYWORD(setenforce,  COMMAND, 1, do_setenforce)
    KEYWORD(setenv,      OPTION,  2, 0)
    KEYWORD(setkey,      COMMAND, 0, do_setkey)
    KEYWORD(setprop,     COMMAND, 2, do_setprop)
    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
    KEYWORD(setsebool,   COMMAND, 1, do_setsebool)
    KEYWORD(socket,      OPTION,  0, 0)
    KEYWORD(start,       COMMAND, 1, do_start)
    KEYWORD(stop,        COMMAND, 1, do_stop)
    KEYWORD(trigger,     COMMAND, 1, do_trigger)
    KEYWORD(symlink,     COMMAND, 1, do_symlink)
    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
    KEYWORD(user,        OPTION,  0, 0)
    KEYWORD(wait,        COMMAND, 1, do_wait)
    KEYWORD(write,       COMMAND, 2, do_write)
    KEYWORD(copy,        COMMAND, 2, do_copy)
    KEYWORD(chown,       COMMAND, 2, do_chown)
    KEYWORD(chmod,       COMMAND, 2, do_chmod)
    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
    KEYWORD(ubiattach,   COMMAND, 1, do_ubiattach)
    KEYWORD(ubidetach,   COMMAND, 1, do_ubidetach)
    KEYWORD(ioprio,      OPTION,  0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
    KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif
以上通過枚舉建立key-value映射,也就相當於map的功能,我們會通過查找key來執行對應的操作

再來查看init_parser.c這個文件,在其中兩次include keywords.h這個文件

#include "keywords.h"//這是得到enum{K_UNKNOWN,K_capability,K_chdir,K_chroot......}

#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

struct {
    const char *name;
    int (*func)(int nargs, char **args);
    unsigned char nargs;
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
#include "keywords.h"//之前已經include得過,此時爲[ K_capability ] = { "capability", 0, 1, OPTION }                                                         //[ K_chdir ] = { "chdir", do_chdir, 2, COMMAND }                                                                                   //[ K_chroot ] = { "chroot", do_chroot, 3, COMMAND}
};
此時keyword_info保存的關於鍵值對的結構體數組

實際上上面兩次include的代碼如下

int do_chroot(int nargs, char **args);
… …
enum
{
K_UNKNOWN,
K_ capability,
K_ chdir,
… …
}
#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
struct {
    const char *name;
    int (*func)(int nargs, char **args);
    unsigned char nargs;
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
    [K_ capability] = {" capability ", 0, 1, OPTION },
    [K_ chdir] = {"chdir", do_chdir ,2, COMMAND},
    … …
};

void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
    switch(kw) {
    case K_service:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on:
        state->context = parse_action(state, nargs, args);//分析對應的on判斷  其中nargs與args對應於命令的參數個數和參數列表,類似main函數
        if (state->context) {
            state->parse_line = parse_line_action;//賦值給每個新行的parse_line
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}


static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct action *act;//這裏查看下面引入的幾個結構體
    if (nargs < 2) {
        parse_error(state, "actions must have a trigger\n");
        return 0;
    }
    if (nargs > 2) {
        parse_error(state, "actions may not have extra parameters\n");
        return 0;
    }//限定nargs只能等於2
    act = calloc(1, sizeof(*act));
    act->name = args[1];
    list_init(&act->commands);//初始化一個commands鏈表
    list_add_tail(&action_list, &act->alist);//將當前act->alist結點添加到action_list鏈表尾部
        /* XXX add to hash */
    return act;
}

static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct command *cmd;
    struct action *act = state->context;
    int (*func)(int nargs, char **args);
    int kw, n;

    if (nargs == 0) {
        return;
    }

    kw = lookup_keyword(args[0]);
    if (!kw_is(kw, COMMAND)) {//查找關鍵字是否爲COMMAND,不是的就返回
        parse_error(state, "invalid command '%s'\n", args[0]);
        return;
    }

    n = kw_nargs(kw);
    if (nargs < n) {
        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
            n > 2 ? "arguments" : "argument");
        return;
    }
    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
    cmd->func = kw_func(kw);//這個時候就有對應的處理函數
    cmd->nargs = nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);//將新建的cmd->clist節點添加到commands尾部                                              }             
對應的鏈表結構圖如下

通常我們定義鏈表都是將結構體變量與指針放在一起,如下所示:

typedef struct DulNode{

     ElemType data;

     struct DulNode *prev;

     struct DulNode *next;

}DulNode, *DuLinkList;

源碼中這種建立鏈表的方式有些特別,建立只有指針的鏈表,將鏈表中的結點放在結構體中,通過求偏移量來訪問結構體變量,提高了效率,值得借鑑

offsetof與container_of可以自己查閱學習


這裏還涉及到一些結構體Action及對應的Command,Service也是如此

struct command
{
        /* list of commands in an action */
    struct listnode clist;


    int (*func)(int nargs, char **args);
    int nargs;
    char *args[1];
};
    
struct action {
        /* node in list of all actions */
    struct listnode alist;
        /* node in the queue of pending actions */
    struct listnode qlist;
        /* node in list of actions for a trigger */
    struct listnode tlist;


    unsigned hash;
    const char *name;
    
    struct listnode commands;
    struct command *current;
};


struct socketinfo {
    struct socketinfo *next;//這裏用到單鏈表形式的結構體指針,用來管理多個socket
    const char *name;
    const char *type;
    uid_t uid;
    gid_t gid;
    int perm;
};


struct svcenvinfo {
    struct svcenvinfo *next;//這裏用到單鏈表形式的結構體指針,管理多個env

    const char *name;
    const char *value;
};


struct service {
        /* list of all services */
    struct listnode slist;//鏈表結點


    const char *name;//服務名
    const char *classname;//class名  如class main等


    unsigned flags;//標誌
    pid_t pid;//分配的進程號
    time_t time_started;    /* time of last start *///service啓動的時間
    time_t time_crashed;    /* first crash within inspection window *///崩潰過程時間
    int nr_crashed;         /* number of times crashed within window *///崩潰次數
    
    uid_t uid;//分配的用戶id
    gid_t gid;//分配的組id
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;


#ifdef HAVE_SELINUX
    char *seclabel;
#endif


    struct socketinfo *sockets;//socket信息結構體
    struct svcenvinfo *envvars;//環境變量結構體


    struct action onrestart;  /* Actions to execute on restart. *///restart時需執行的action
    
    /* keycodes for triggering this service via /dev/keychord */
    int *keycodes;
    int nkeycodes;
    int keychord_id;


    int ioprio_class;
    int ioprio_pri;


    int nargs;
    /* "MUST BE AT THE END OF THE STRUCT" */
    char *args[1];
}; /*     ^-------'args' MUST be at the end of this struct! */                                                                                                                                 

查看分析service的源碼

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;
    if (nargs < 3) {//判斷服務參數個數
        parse_error(state, "services must have a name and a program\n");
        return 0;
    }
    if (!valid_name(args[1])) {//判斷服務名是否有效
        parse_error(state, "invalid service name '%s'\n", args[1]);
        return 0;
    }

    svc = service_find_by_name(args[1]);//判斷是否已經定義
    if (svc) {
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

    nargs -= 2;
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    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);//初始化一個action onrestart的commands雙向鏈表
    list_add_tail(&service_list, &svc->slist);//將當前svc->slist結點添加至service_list鏈表
    return svc;
}

static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc = state->context;
    struct command *cmd;
    int i, kw, kw_nargs;

    if (nargs == 0) {
        return;
    }

    svc->ioprio_class = IoSchedClass_NONE;

    kw = lookup_keyword(args[0]);
    switch (kw) {
    case K_capability:
        break;
    case K_class:
        if (nargs != 2) {
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;
    case K_console:
        svc->flags |= SVC_CONSOLE;//設置flags爲SVC_CONSOLE
        break;
    case K_disabled:
        svc->flags |= SVC_DISABLED;//設置flags
        svc->flags |= SVC_RC_DISABLED;
        break;
    case K_ioprio:
        if (nargs != 3) {
            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
        } else {
            svc->ioprio_pri = strtoul(args[2], 0, 8);

            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
                parse_error(state, "priority value must be range 0 - 7\n");
                break;
            }

            if (!strcmp(args[1], "rt")) {
                svc->ioprio_class = IoSchedClass_RT;
            } else if (!strcmp(args[1], "be")) {
                svc->ioprio_class = IoSchedClass_BE;
            } else if (!strcmp(args[1], "idle")) {
                svc->ioprio_class = IoSchedClass_IDLE;
            } else {
                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
            }
        }
        break;
    case K_group:
        if (nargs < 2) {
            parse_error(state, "group option requires a group id\n");
        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
            parse_error(state, "group option accepts at most %d supp. groups\n",
                        NR_SVC_SUPP_GIDS);
        } else {
            int n;
            svc->gid = decode_uid(args[1]);
            for (n = 2; n < nargs; n++) {
                svc->supp_gids[n-2] = decode_uid(args[n]);
            }
            svc->nr_supp_gids = n - 2;
        }
        break;
    case K_keycodes:
        if (nargs < 2) {
            parse_error(state, "keycodes option requires atleast one keycode\n");
        } else {
            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
            if (!svc->keycodes) {
                parse_error(state, "could not allocate keycodes\n");
            } else {
                svc->nkeycodes = nargs - 1;
                for (i = 1; i < nargs; i++) {
                    svc->keycodes[i - 1] = atoi(args[i]);
                }
            }
        }
        break;
    case K_oneshot:
        svc->flags |= SVC_ONESHOT;
        break;
    case K_onrestart:
        nargs--;
        args++;
        kw = lookup_keyword(args[0]);
        if (!kw_is(kw, COMMAND)) {
            parse_error(state, "invalid command '%s'\n", args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);
        if (nargs < kw_nargs) {
            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
                kw_nargs > 2 ? "arguments" : "argument");
            break;
        }

        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
        cmd->func = kw_func(kw);
        cmd->nargs = nargs;
        memcpy(cmd->args, args, sizeof(char*) * nargs);
        list_add_tail(&svc->onrestart.commands, &cmd->clist);
        break;
    case K_critical:
        svc->flags |= SVC_CRITICAL;
        break;
    case K_dalvik_recache:
        svc->flags |= SVC_DALVIK_RECACHE;
        break;
    case K_setenv: { /* name value */
        struct svcenvinfo *ei;
        if (nargs < 2) {
            parse_error(state, "setenv option requires name and value arguments\n");
            break;
        }
        ei = calloc(1, sizeof(*ei));
        if (!ei) {
            parse_error(state, "out of memory\n");
            break;
        }
        ei->name = args[1];
        ei->value = args[2];
        ei->next = svc->envvars;//單鏈表操作
        svc->envvars = ei;//單鏈表操作
        break;
    }
    case K_socket: {/* name type perm [ uid gid ] */
        struct socketinfo *si;
        if (nargs < 4) {
            parse_error(state, "socket option requires name, type, perm arguments\n");
            break;
        }
        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
                && strcmp(args[2],"seqpacket")) {
            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
            break;
        }
        si = calloc(1, sizeof(*si));
        if (!si) {
            parse_error(state, "out of memory\n");
            break;
        }
        si->name = args[1];
        si->type = args[2];
        si->perm = strtoul(args[3], 0, 8);
        if (nargs > 4)
            si->uid = decode_uid(args[4]);
        if (nargs > 5)
            si->gid = decode_uid(args[5]);
        si->next = svc->sockets;//這種插入方式是逆序插入
        svc->sockets = si;//將新鏈表表頭賦值給sockets
        break;
    }
    case K_user:
        if (nargs != 2) {
            parse_error(state, "user option requires a user id\n");
        } else {
            svc->uid = decode_uid(args[1]);
        }
        break;
    case K_seclabel:
#ifdef HAVE_SELINUX
        if (nargs != 2) {
            parse_error(state, "seclabel option requires a label string\n");
        } else {
            svc->seclabel = args[1];
        }
#endif
        break;

    default:
        parse_error(state, "invalid option '%s'\n", args[0]);
    }
}


同理,根據service解析代碼我們也能畫出service_list的結構圖,不過比較特殊的是service  option項比較多  情況各異  有很大出入



從以上代碼可以看出,paser_action主要解析一個Action剛開始的情況並添加到action_list鏈表,paser_line_action則解析Action中的command並添加到command鏈表

service的解析函數亦同理

最後分析import加入的新rc文件,init.rc文件便解析完成,並將所有的action和service分別添加到action_list和service_list鏈表


跟隨代碼,下面執行這些函數,這裏可能有些疑惑,上面明顯聲明瞭三個鏈表,但是一直都沒有涉及到action_queue這個鏈表。

    
    action_for_each_trigger("early-init",action_add_queue_tail);
    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
  

分析這兩個個函數看到底做了什麼處理,其中wait_for_coldboot_done_action是一個執行函數

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) {//遍歷已經完整的action_list鏈表,查找early-init action
        act = node_to_item(node, struct action, alist);
        if (!strcmp(act->name, trigger)) {
            func(act);//執行action_add_queue_tail
        }
    }
}

void action_add_queue_tail(struct action *act)
{
    list_add_tail(&action_queue, &act->qlist);//將early-init action中的qlist結點添加到action_queue鏈表中(這裏開始涉及到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));//首先新建一個action
    act->name = name;
    list_init(&act->commands);//初始化commands鏈表


    cmd = calloc(1, sizeof(*cmd));//新建一個command結構體
    cmd->func = func;
    cmd->args[0] = name;
    list_add_tail(&act->commands, &cmd->clist);//將cmd->clist結點添加到commands鏈表尾部


    list_add_tail(&action_list, &act->alist);//將act->alist結點添加到上面的action_list尾部
    action_add_queue_tail(act);//將這個action添加到action_queue鏈表尾部
}                                                                                                                                      

從以上代碼分析可看出action_for_each_trigger函數實現查找action_list中的action,並將其添加到action_queue尾部

queue_builtin_action則是新建一個action,將其分別添加到action_list和action_queue鏈表尾部

    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);
        queue_builtin_action(ubootenv_init_action, "ubootenv_init");
        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");




#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif

從以上的代碼實現的功能都是類似的

接着閱讀init.c後面的源碼

    for(;;) {
        int nr, i, timeout = -1;

        execute_one_command();//從鏈表中取出結點相應執行然後remove
        //分析過這個函數,在這裏還有個疑問,該函數都是從action隊列中去結點執行,但是系統的service是怎麼執行的
        //難道service鏈表不可能只註冊不執行
        //這裏注意on boot section中最後一個command(class_start default),最終調用do_class_start


  

static struct command *get_first_command(struct action *act)
{
    struct listnode *node;
    node = list_head(&act->commands);
    if (!node || list_empty(&act->commands))
        return NULL;


    return node_to_item(node, struct command, clist);
}                                                                                                                                                                                                                                                             static struct command *get_next_command(struct action *act, struct command *cmd)
{
    struct listnode *node;
    node = cmd->clist.next;
    if (!node)
        return NULL;
    if (node == &act->commands)
        return NULL;

    return node_to_item(node, struct command, clist);
}

static int is_last_command(struct action *act, struct command *cmd)
{
    return (list_tail(&act->commands) == &cmd->clist);//判斷cmd->clist結點是否爲act->commands鏈表最後一個
}

void execute_one_command(void)
{
    int ret;
    //第一次執行cur_action是action結構體指針,cur_command是command結構體指針,都爲null
    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();//從非空action_queue鏈表中取出頭部結點並移除
        cur_command = NULL;
        if (!cur_action)//cur_action爲null時返回
            return;
        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
        cur_command = get_first_command(cur_action);//從cur_action中取出第一個command
    } else {
        cur_command = get_next_command(cur_action, cur_command);//依次取出後面的command
    }

    if (!cur_command)//cur_command爲null時返回
        return;

    ret = cur_command->func(cur_command->nargs, cur_command->args);//這裏纔開始執行command操作
    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}
   
當執行完一個action中的所有command後,通過for(;;)再依次執行action_queue中的下一個action

因此action_queue鏈表中的順序就是系統真正的執行順序,如圖所示



到這裏,大體上弄清楚了init的執行過程,但是這裏有個疑問,所有的action都已經執行完畢,根本沒有涉及到service

查看init.rc文件我們可以看到在on boot這個action中對應的command爲 

    class_start core
    class_start main

根據之前的parse_line_action我們可以跟蹤到do_class_start函數

int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

void service_for_each_class(const char *classname,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {//遍歷service_list鏈表
        svc = node_to_item(node, struct service, slist);//從service_list鏈表中返回對應的service結構體
        if (!strcmp(svc->classname, classname)) {//比較classname是否爲core,main等
            func(svc);
        }
    }
}
以上的classname爲parse_line_service函數中解析的,即service中class一項
static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {//判斷svc的flags是否爲DISABLED
        service_start(svc, NULL);
    }
}

void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;

        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    NOTICE("starting '%s'\n", svc->name);
    //以上主要設置該服務的一些標誌
    pid = fork();//通過fork()創建子進程

    if (pid == 0) {//此爲子進程
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        if (properties_inited()) {
            get_property_workspace(&fd, &sz);//獲取屬性系統句柄
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);//爲該服務添加環境變量

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,//創建通信socket,相當於每個service都創建socket,用來與其它進程通信
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
                publish_socket(si->name, s);//將句柄s添加到環境變量中,該環境變量爲ANDROID_SOCKET_XXX
            }
        }

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {//設置pid
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();//打開控制檯
        } else {
            zap_stdio();
        }

#if 0
        for (n = 0; svc->args[n]; n++) {
            INFO("args[%d] = '%s'\n", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", n, ENV[n]);
        }
#endif
        //配置進程id和組
        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);//執行新進程調用的函數
        }
        _exit(127);
    }

    if (pid < 0) {//fork()錯誤
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

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

    if (properties_inited())
        notify_service_state(svc->name, "running");//設置服務運行狀態
}

從上面可以看出service的運行過程,同理,service_list鏈表也得到執行

execute_one_command函數在for循環中一直查找action_queue鏈表中是否爲空,不爲空的情況下就移除隊首的結點並執行,否則就直接返回


        restart_processes();//判斷是否有進程需要重啓

        if (!property_set_fd_init && get_property_set_fd() > 0) {//系統屬性
            ufds[fd_count].fd = get_property_set_fd();//獲取property系統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();//獲取子進程信號處理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();//獲取組合鍵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 BOOTCHART
        if (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;
            }
        }
#endif

        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;
}

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}
                                                                                                                                    void service_for_each_flags(unsigned matchflags,
                            void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {//遍歷service_list鏈表
        svc = node_to_item(node, struct service, slist);
        if (svc->flags & matchflags) {//判斷服務標誌是否爲RESTARTING,成立就回調函數執行
            func(svc);
        }
    }
}                                                                                                                              
                                                                                                                                    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);//清除RESTARTING標誌
        service_start(svc, NULL);
        return;
    }

    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

這樣一個服務就會被重啓,但是死亡的服務它的標誌是怎麼樣被設置成RESTARTING,這裏有疑惑

從後面的代碼可以看出,inti採用I/O多路服用才監聽3個句柄的情況,當可讀時做相應的處理

在多路服用中,如果timeout==0 poll就不阻塞;如果timeout>0,poll只有當等待時間超時或有事件發生時才返回;如果timeout==-1就有事件發生纔會返回


if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;//設置等待時間
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action)//如果action_queue和cur_action都不爲空,timeout設爲0,此時不阻塞,相當於此時不執行poll操作
            timeout = 0;

#if BOOTCHART
        if (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;
            }
        }
#endif

        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)//如果超時等待,就不執行後面的處理,直接跳到for循環開始處,執行action或者重啓service
            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();
            }
        }
    }
這裏主要處理三大事件:屬性設置事件,按鍵組合事件,子進程信號事件。前兩者這裏不做討論,按鍵組合事件也只有在調試模式下才處理

從上面的分析中我們知道service是init調用fork創建的子進程,在Linux進程間通信中,可以通過SIGCHLD信號來通知子進程的狀態

在之前的action_queue已經進行signal_init初始化

void signal_init(void)
{
    int s[2];

    struct sigaction act;

    act.sa_handler = sigchld_handler;//信號處理函數
    act.sa_flags = SA_NOCLDSTOP;
    act.sa_mask = 0;
    act.sa_restorer = NULL;
    sigaction(SIGCHLD, &act, 0);//安裝SIGCHLD信號處理器

    /* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {//用於init進程中雙端之間通信
        signal_fd = s[0];//發送端socket fd
        signal_recv_fd = s[1];//接收端socket fd,並且被註冊到poll系統監聽
        fcntl(s[0], F_SETFD, FD_CLOEXEC);//設置fd的屬性
        fcntl(s[0], F_SETFL, O_NONBLOCK);//非阻塞
        fcntl(s[1], F_SETFD, FD_CLOEXEC);
        fcntl(s[1], F_SETFL, O_NONBLOCK);
    }

    handle_signal();
}

static void sigchld_handler(int s)
{
    write(signal_fd, &s, 1);
}

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_one_process,直到返回非0
        ;
}
套接字可用於網絡通信,也可以用於本機內的進程通信。由於本機內進程的IP地址都相同,因此只需要進程號來確定通信的雙方。非網絡通信套接字在Android系統中應用很多。Linux環境下使用socketpair函數創造一對未命名的、相互連接的UNIX域套接字。

在init進程中初始化signal_init,在init子進程死亡後會向init進程發送SIGCHLD信號,在init進程中已經註冊該信號處理器sigchld_handler,在該函數中會向signal_fd發送信號的編號,而在另一端則接收這個數據,由於signal_recv_fd已註冊在poll中,因此會調用handle_signal進行處理



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;
    //waitpid函數停止當前進程,等待子進程的結束,-1表示等待任何子進程,WNOHANG表示返回該進程的id,status爲返回狀態
    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
    if (pid <= 0) return -1;//進程號不可能爲負數,此時while循環退出
    INFO("waitpid returned pid %d, status = %08x\n", pid, status);

    svc = service_find_by_pid(pid);//通過pid從service_list中查找結點
    if (!svc) {
        ERROR("untracked pid %d exited\n", pid);
        return 0;
    }

    NOTICE("process '%s', pid %d exited\n", svc->name, pid);
    //判斷service是否爲oneshot,如果是代表只運行一次,則不需要再重啓
    if (!(svc->flags & SVC_ONESHOT)) {//如果service不爲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);//釋放該服務佔用的所有socket資源

    }

    svc->pid = 0;//設置進程號爲0
    svc->flags &= (~SVC_RUNNING);//清除狀態標誌RUNNING

        /* oneshot processes go into the disabled state on exit */
    if (svc->flags & SVC_ONESHOT) {//如果設置了狀態ONESHOT,則不需要重啓,設置爲DISABLED
        svc->flags |= SVC_DISABLED;
    }

        /* disabled and reset processes do not get restarted automatically */
    if (svc->flags & (SVC_DISABLED | SVC_RESET) )  {//如果狀態設置了DISABLED或者RESET,則不需要重啓
        notify_service_state(svc->name, "stopped");//設置狀態屬性值爲stopped
        return 0;
    }

    now = gettime();
    if (svc->flags & SVC_CRITICAL) {//如果service標誌爲CRITICAL
        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {//如果崩潰時間超過4分鐘
            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {//如果崩潰次數超過4次
                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;
        }
    }else if (svc->flags & SVC_DALVIK_RECACHE) {
        if (svc->time_started + RECACHE_ENABLE_PHASE >= now) {
            ERROR("recacheabl process '%s' exited at(%lu) ,start(%lu)",
                  svc->name, now, svc->time_started);
            system("/system/xbin/busybox rm /data/dalvik-cache/*");
            //android_reboot(ANDROID_RB_RESTART, 0, 0);
        }
    }

    svc->flags |= SVC_RESTARTING;//設置service標誌爲RESTARTING,待restart_processes()函數重啓該服務

    /* 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");//修改狀態屬性值爲restarting
    return 0;
}

init進程進入死循環,監聽三大事件,並查詢action_queue與service_list鏈表,是否有action需要執行,是否有service需要重啓,並進行處理。

至此,分析完畢。



1.三大鏈表action_list,action_queue,service_list, action_queue纔是真正用來查詢執行的,因此它決定執行順序

2.注意源碼中node_to_item,該宏通過鏈表節點返回結構體引用

3.service_list中的service是何時才被執行,怎樣執行的

4.init進程死循環中,execute_one_command()和restart_processes()函數是怎麼執行action和重啓service_list服務的,尤其是對service重啓的處理




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章