AndroidInitProcess分析心得(1)

[cpp] 

眾所皆知,Android Init process是Android啟動後最先起來的進程. 真正來說Android Init process是由Linux Kernel的啟動程序所驅動起來. 從device上電, Bootloader加載Kernel, 然後Kernel接著驅動Android Init process. 這一段屬於Linux 的范疇, 其簡單的函數呼叫流程如下:

 

kernel_init===> init_post ==> run_init_process ==> 啟動 Android Init process.

 

由於這篇主要是分析AndroidInit process在處始化所作的工作, 因此kernel_init, init_post,run_init_process 這三個函數裡的流程就不在這裡作分析.

 

        Android Init process 在初始化階段主要做三件事.

1. 分析和執行init.rc腳本文件

2. 創建devicenode file

3. 監控系統屬性變化跟事件

以下就分別依照這三點來做研究分析.

分析和執行init.rc腳本文件

        init.rc腳本文件主要是用來設定Android系統環境,還有一些待執行的進程記錄.整個腳本文件可以分為兩類action list跟 service list. 這兩類會根據腳本文件中的關鍵詞來作分類,

        1. 以"on"關鍵詞開頭的為actionlist中的元素,

        2. 以"Services"關鍵詞的為servicelist中的元素.

這兩個關鍵詞就跟init.rc腳本文件使用的AIL(Android Init Language)有關瞭.AIL 主要包含四種類型, Action, Commands, Services, Option. 這四類的語法用法在system\core\init\readme.txt中有詳細描述. 這裡隻是簡單的介紹這四類的關系, 語法用法請參考system\core\init\readme.txt.

        Action和Services代表著一段新的Section,所有的Section下都有Command跟Option的一些宣告.Command最主要是用來創建一些系統目錄或是啟動進程.Option則作為ServicesSection的一些屬性設定. 比如進程是否從新被啟動.

        分析和執行init.rc腳本文件的研究分析就由init_parse_config_file函數開始.因為此函數正是用來init.rc腳本文件作分析流程.

[cpp]
// \system\core\init\init_parser.c  
int init_parse_config_file(const char *fn) 

    char *data; 
    data = read_file(fn, 0); 
    if (!data) return -1; 
 
    parse_config(fn, data); 
    DUMP(); 
    return 0; 

 
static void parse_config(const char *fn, char *s) 

   struct parse_state state; 
   // …  
   state.filename = fn; 
   state.line = 0; 
   state.ptr = s; 
   state.nexttoken = 0; 
   state.parse_line = parse_line_no_op; 
   // …  
   for (;;) { 
      switch (next_token(&state)) { 
      case T_EOF: 
          state.parse_line(&state, 0, 0); 
          goto parser_done; 
      case T_NEWLINE: 
            state.line++; 
            if (nargs) { 
                int kw = lookup_keyword(args[0]); 
                if (kw_is(kw, SECTION)) { 
                    state.parse_line(&state, 0, 0); 
                    parse_new_section(&state, kw, nargs, args); 
                } else { 
                    state.parse_line(&state, nargs, args); 
                } 
                nargs = 0; 
            } 
            break; 
        case T_TEXT: 
            if (nargs < INIT_PARSER_MAXARGS) { 
                args[nargs++] = state.text; 
            } 
            break; 
      } 
   } 

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); 
        if (state->context) { 
            state->parse_line = parse_line_action; 
            return; 
        } 
        break; 
    case K_import: 
        parse_import(state, nargs, args); 
        break; 
    } 
    state->parse_line = parse_line_no_op; 

// \system\core\init\init_parser.c
int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);
    if (!data) return -1;

    parse_config(fn, data);
    DUMP();
    return 0;
}

static void parse_config(const char *fn, char *s)
{
   struct parse_state state;
   // …
   state.filename = fn;
   state.line = 0;
   state.ptr = s;
   state.nexttoken = 0;
   state.parse_line = parse_line_no_op;
   // …
   for (;;) {
      switch (next_token(&state)) {
      case T_EOF:
          state.parse_line(&state, 0, 0);
          goto parser_done;
      case T_NEWLINE:
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
      }
   }
}
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);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}

 

由上面的程序代碼可以很清楚的看到, 隻是把init.rc腳本文件中的section中的command跟option的動作加入actionlist和service list等待執行.加入的動作可以研究 parse_action 和 parse_service的實作.分析流程先到此, 之後再來看parse_action 和 parse_service的實作.

        以上是作init.rc腳本文件的分析流程, 而init.rc腳本文件的執行流程就由execute_one_command 函數來實作.

[cpp]
/ \system\core\init\init.c  
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); 

// \system\core\init\init.c
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);
}

此函數執行到最後是利用cur_command所帶的function去執行actionlist中的command. 此cur_command所帶的function到底是甚麼呢? 可以由前面的呼叫函數 get_first_command和 get_next_command去推導.

[cpp]
// \system\core\init\init.c  
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); 

// \system\core\init\init.c
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);
}

到此我們隻知道這兩個函數隻是從action list把actioncommand node的數據取出來, 至於這個command node所帶的function還是不知道怎麼來的?

        隻好在往分析流程去找蛛絲馬跡瞭. 在分析流程中一直執行到利用parse_config函數來做解析init.rc的動作時, 有發現在呼叫parse_new_section之前會需要先執行lookup_keyword函數去取得一個keyword當作參數. 就來從這函數開始分析

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。