Android init.rc on property
阿新 • • 發佈:2019-01-24
在 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)... 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);
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 了 驗證了實驗結論中的:系統執行中如果屬性發生變化,且新值等於設定值,則觸發一次