Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android啟動流程分析-解析init.rc的service

Android啟動流程分析-解析init.rc的service

編輯:關於Android編程

 

在分析完解析init.rc的action之後,剩下的一部分就是解析service了。

而解析service還是需要回到parse_config裡面來。根據前面的知識,我們也可以很容易的知道在關鍵字為section的時候,會進入到parse_new_section。

這裡會先執行parse_service,然後將service以及後面跟的option設置為執行parse_line:parse_line_service。

要理解service的解析流程的話,首先要關注的就是service的結構體。

 

struct service {
    /* list of all services */
    struct listnode slist;   // listnode slist

    const char *name;        // name
    const char *classname;   // 默認值為defult

    unsigned flags;          // 選項
    pid_t pid;               // Service所在進程的PID
    time_t time_started;    /* time of last start */
    time_t time_crashed;    /* first crash within inspection window */
    int nr_crashed;         /* number of times crashed within window */

    uid_t uid;               // effective user ID
    gid_t gid;               // effective group ID
    gid_t supp_gids[NR_SVC_SUPP_GIDS];  // supplementary ids
    size_t nr_supp_gids;     // supp_gids的大小

    char *seclabel;

    struct socketinfo *sockets;  // 為service創建的Sockets
    struct svcenvinfo *envvars;  // 為service設置的環境變量

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

    /* 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的本身屬性以外,對於數據結構方面就只有一個listnode。

 

struct listnode slist; 
這也就是說,在service的結構體中,這個結構體只會被加入一條鏈表而不是像action的兩條鏈表。

 

另外需要注意的是,在service的結構體中,也維護了一個action的結構體,這就是說,在service中,也存在著一個action的commands的鏈表?

然後我們就來看看parse_service的函數

 

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;  // 聲明結構體
    if (nargs < 3) { // 如果service的參數小於三個的話,我們會認為service是個不正常的service。service最少的nargs也是3,分別為service關鍵字,service的名字,service啟動的時候要執行的命令
        parse_error(state, services must have a name and a program
);
        return 0;
    }
    if (!valid_name(args[1])) {  //如果service的name為不標准的名字的話,含有其它的符號的話,我們會認為這個service是不規范的service。
        parse_error(state, invalid service name '%s'
, args[1]);
        return 0;
    }

    svc = service_find_by_name(args[1]); 。。// 會從已經存在的service_list裡面去查找,是否已經有同名的service存在
    if (svc) {  // 如果發現有同名的service存在的話,則會返回error
        parse_error(state, ignored duplicate definition of service '%s'
, args[1]);
        return 0;
    }

    nargs -= 2;  // 去除service關鍵字與service的name
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); // malloc這個結構體
    if (!svc) { // 如果malloc失敗的話,提示out of memory
        parse_error(state, out of memory
);
        return 0;
    }
    svc->name = args[1];  // 設置service的name為service關鍵字後的第一個參數
    svc->classname = default;  // 默認的classname為default
    memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 將args剩余的參數復制到svc的args裡面
    svc->args[nargs] = 0;  // 給args的最後一項設置為0
    svc->nargs = nargs;  // 參數的數目等於傳進來的參數的數目
    svc->onrestart.name = onrestart; // 設置onrestart.name為onrestart
    list_init(&svc->onrestart.commands); // 初始化onrestart的鏈表
    list_add_tail(&service_list, &svc->slist);  // 將當前的service結構體加入到了service_list的鏈表裡面
    return svc;
}
從上面我們知道,在執行完parse_service之後,會初始化service的一些屬性,將service servicename之後的做為args進行保存。

 

另外,將這個解析出來的service加入到service_list的鏈表裡面。

然後接下來,去執行的就是

 

state->parse_line = parse_line_service;
那我們像action,再來看看parse_line_service的操作

 

 

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
);
        } else {
            svc->classname = args[1];
        }
        break;
    case K_console:
        svc->flags |= SVC_CONSOLE;
        break;
    case K_disabled:
        svc->flags |= SVC_DISABLED;
        svc->flags |= SVC_RC_DISABLED;
        break;
    case K_ioprio:
        if (nargs != 3) {
            parse_error(state, ioprio optin usage: ioprio  
);
        } else {    。。。。                                                                                                                                                                                                                                     ........                                                                                                                                                                                                                                                          case K_onrestart:
        nargs--;
        args++;
        kw = lookup_keyword(args[0]);
        if (!kw_is(kw, COMMAND)) {
            parse_error(state, invalid command '%s'
, args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);
        if (nargs < kw_nargs) {
            parse_error(state, %s requires %d %s
, 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;                                                                                                                                                                                                                                                                                              ...............                                                                                                                                                                                                                                                                                 }

可以看到,parse_line_service的這個函數,主要就是將解析service的每一行,將其對應進不同的case裡面,進行service結構體的填充。

可能這樣講,理解起來會有困難。

但是為了方便理解,我們從init.rc裡面找個例子出來分析:

 

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
在parse_line_service的時候,會在

 

class的關鍵字的時候,執行到

    case K_class:
        if (nargs != 2) { // 判斷是否是兩個token,如果不是的話,格式錯誤
            parse_error(state, class option requires a classname
);
        } else {
            svc->classname = args[1];
        }
        break;
也就是將service的classname從default修改為“args[1]”
在user system的的option的時候,

 

會去執行

 

    case K_user:
        if (nargs != 2) {
            parse_error(state, user option requires a user id
);
        } else {
            svc->uid = decode_uid(args[1]);
        }
        break;
會將uid設置為system對應的uid

 

另外需要注意的是,在解析的過程中,會有一個比較重要的option是restart,來看一下當執行到這個關鍵字的時候的時候,會運行什麼。

 

    case K_onrestart:
        nargs--;
        args++; 
        kw = lookup_keyword(args[0]);  
        if (!kw_is(kw, COMMAND)) {  // 判斷是否是command,如果restart之後跟的不是command的話,就會返回error
            parse_error(state, invalid command '%s'
, args[0]);
            break;
        }
        kw_nargs = kw_nargs(kw);  // 獲得當前command所需的參數
        if (nargs < kw_nargs) { // 如果傳遞的參數小於我們需要的參數的話,會返回error
            parse_error(state, %s requires %d %s
, args[0], kw_nargs - 1,
                kw_nargs > 2 ? arguments : argument);
            break;
        }

        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); // 初始化command
        cmd->func = kw_func(kw);   // 將kw所包含的func賦值給cmd->func
        cmd->nargs = nargs;  // 將參數的個數保存為nargs
        memcpy(cmd->args, args, sizeof(char*) * nargs); // 將這些參數復制到cmd的args中
        list_add_tail(&svc->onrestart.commands, &cmd->clist); // 將這些command加入到service結構體的內部鏈表中
        break;

至此,我們分析了所有關於init.rc的解析問題。

 

在service的解析後,會生成一條鏈表保存service的結構體,然後service的結構體裡面自己運行維護一個action。

這個action會包含所有的restatt包含的內容,也就是restart的option關鍵字後會包含要執行的command

這個的結構應該比action的要簡單和明了的多。

分析完了解析,我們應該去看一下android是如何在啟動的過程中去執行這些action和service的。

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved