1. 程式人生 > >Android init.rc on property

Android init.rc on property

在 init.rc 中,可以見到下面類似的用法,當一個屬性值等於XX時,觸發下面的事件,比如啟動一個程序,或者設定列印級別

on property:sys.init_log_level=*     loglevel ${sys.init_log_level} 那麼它是如何實現的,啟動時觸發一次?還是任何時刻只要屬性值滿足條件就觸發?  實驗驗證結果: 1、啟動時,如果屬性滿足設定條件會觸發一次 2、系統執行中如果屬性發生變化,且新值等於設定值,則觸發一次 程式碼分析: init.c (system\core\init) int main(int argc, char **argv)
{     ...     //分配存放屬性的共享記憶體     property_init();
    ...     INFO("property init\n");     property_load_boot_defaults(); //載入預設的屬性     INFO("reading config file\n");     init_parse_config_file("/init.rc");//解析init.rc檔案     ...     //處理 on early-init\init section部分,本文暫不關心     action_for_each_trigger("early-init", action_add_queue_tail);
    action_for_each_trigger("init", action_add_queue_tail);     //這個是重點     queue_builtin_action(property_service_init_action, "property_service_init");     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");     //陷入死迴圈,等待接收別的程序設定屬性     for (;;)     {         int nr, i, timeout = -1;
        //重點稍後分析         execute_one_command();         restart_processes();         //使用poll輪訓是否有程序設定屬性         ...         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; } .rc 檔案中關於類似 on property:sys.init_log_level=* 的解析過程: int init_parse_config_file(const char *fn)     parse_config(fn, data);     return 0; } static void parse_config(const char *fn, char *s) {     ...     for (;;) {         switch (next_token(&state)) {         ...
        case T_NEWLINE:             //如果是新的一行             state.line++;             if (nargs) {                 int kw = lookup_keyword(args[0]);                 if (kw_is(kw, SECTION)) {    //判斷是否是section                     state.parse_line(&state, 0, 0); parse_new_section(&state, kw, nargs, args);                 } else {                     //不是section,那就是command                     state.parse_line(&state, nargs, args);                 }                 nargs = 0;             }             break;         ...
    } parser_done:     ...
} static 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; } .rc 檔案中有三種 section 1:on 開頭的  on property:sys.init_log_level=*     loglevel ${sys.init_log_level} 2:service 開頭的      service netd /system/bin/netd 3:import開頭的 import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /init.${ro.zygote}.rc import /init.trace.rc 這裡,我們只關心 on property:sys.init_log_level=* 這種 static void *parse_action(struct parse_state *state, int nargs, char **args) {     struct action *act; //解析 section 構造一個 action 掛入 action_list     act = calloc(1, sizeof(*act));     act->name = args[1];     list_init(&act->commands);     list_init(&act->qlist);     list_add_tail(&action_list, &act->alist);         /* 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; //解析這個 section 對應的 commond 掛入 action 的 commands 連結串列     cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);     cmd->func = kw_func(kw);     cmd->line = state->line;     cmd->filename = state->filename;     cmd->nargs = nargs;     memcpy(cmd->args, args, sizeof(char*) * nargs);     list_add_tail(&act->commands, &cmd->clist); } 也就是說,在解析完 .rc 檔案以後,action_list 連結串列中填充了大量的 action ,名字類似於property:sys.init_log_level=*,並且可以找到它們對應的 command . queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); void queue_builtin_action(int (*func)(int nargs, char **args), char *name) {     struct action *act;     struct command *cmd;     //和解析 .rc 檔案一樣,構造 action command 放入 action_list     act = calloc(1, sizeof(*act));     act->name = name;     list_init(&act->commands);     list_init(&act->qlist);     cmd = calloc(1, sizeof(*cmd));     cmd->func = func;     cmd->args[0] = name;     cmd->nargs = 1;     list_add_tail(&act->commands, &cmd->clist);     list_add_tail(&action_list, &act->alist);     action_add_queue_tail(act); } 值得注意的是,這裡還呼叫了 action_add_queue_tail(act) 將該 action 掛入了 action_queue 連結串列,在main函式最後的for迴圈中,將取出 action_queue 連結串列中的 action ,呼叫它們的 command ,後面在分析 這裡構造了名字為property_service_init、queue_property_triggers的兩個 action ,分別都放入action_list\action_queue連結串列 來看陷入for迴圈之後的工作: for (;;)     {         int nr, i, timeout = -1;         //重點稍後分析         execute_one_command();         restart_processes();                //使用poll輪訓是否有程序設定屬性         ...         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(); }             }         }     } 1 execute_one_command(); void execute_one_command(void) {     if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {         cur_action = action_remove_queue_head(); //從 action_queue 連結串列中取出 action          cur_command = NULL;     }      ret = cur_command->func(cur_command->nargs, cur_command->args);//執行 action 的 command } 現在來分析一下前面的: queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); 這裡就會呼叫 property_service_init_action 、queue_property_triggers_action static int property_service_init_action(int nargs, char **args) {     start_property_service(); //建立用於獲取屬性的 socket,不是關心的重點     return 0; } static int queue_property_triggers_action(int nargs, char **args) {     queue_all_property_triggers();     property_triggers_enabled = 1;     return 0; } void queue_all_property_triggers() {     struct listnode *node;     struct action *act; list_for_each(node, &action_list) {         act = node_to_item(node, struct action, alist);         if (!strncmp(act->name, "property:", strlen("property:"))) {             /* parse property name and value                syntax is property:<name>=<value> */             const char* name = act->name + strlen("property:");             const char* equals = strchr(name, '=');             if (equals) {                 char prop_name[PROP_NAME_MAX + 1];                 char value[PROP_VALUE_MAX];                 int length = equals - name;                 if (length > PROP_NAME_MAX) {                     ERROR("property name too long in trigger %s", act->name);                 } else {                     int ret; memcpy(prop_name, name, length);                     prop_name[length] = 0;                     /* 如果對應的屬性存在,判斷是否等於設定的值,或者是* */                     ret = property_get(prop_name, value);                     if (ret > 0 && (!strcmp(equals + 1, value) ||                                     !strcmp(equals + 1, "*"))) {                         //如果判斷成功,掛入 action_queue                         action_add_queue_tail(act);                     }                 }             }         }     } } 舉例分析:property:sys.init_log_level=* name sys.init_log_level value * 如果系統中存在 sys.init_log_level 這個屬性,就將這個 action 掛入 action_queue 那麼,在下次呼叫 execute_one_command 的時候,就會呼叫 sys.init_log_level 對應的 command loglevel ${sys.init_log_level} 設定列印級別 也就驗證了實驗結論,在系統啟動過程中,如果存在滿足條件的屬性,則觸發一次! 下次 execute_one_command 什麼時候呼叫?很簡單,poll超時返回時,就回到for的起點,呼叫了! poll 有資料時:handle_property_set_fd handle_property_set_fd     property_set((char*) msg.name, (char*) msg.value);         //設定屬性         property_changed(name, value); void property_changed(const char *name, const char *value) {     if (property_triggers_enabled) //前面 queue_property_triggers_action 函式中已經設定為1         queue_property_triggers(name, value); } void queue_property_triggers(const char *name, const char *value) {     struct listnode *node;     struct action *act;     list_for_each(node, &action_list) {         act = node_to_item(node, struct action, alist);         if (!strncmp(act->name, "property:", strlen("property:"))) {             const char *test = act->name + strlen("property:");             int name_length = strlen(name);             if (!strncmp(name, test, name_length) &&                     test[name_length] == '=' &&                     (!strcmp(test + name_length + 1, value) ||                      !strcmp(test + name_length + 1, "*"))) {                 action_add_queue_tail(act);             }         }     } } 拿新設定的屬性的名字和action_list中存在的每一個action的名字做比較(含有property:的action),如果值命中了那麼放入 action_queue 連結串列 在execute_one_command 中就會呼叫 commmand 了 驗證了實驗結論中的:系統執行中如果屬性發生變化,且新值等於設定值,則觸發一次