Android启动流程分析(八) 解析init.rc的action

#############################################

本文为极度寒冰原创,转载请注明出处

#############################################

上一章讲述了android启动过程中,加载init.rc之后需要对其进行解析。

而解析又根据三个不同的SECTION来执行不同的初始化的文件,,分别是parse_action,parse_service,parse_import.

那么,这一节,我们就从parse_action来讲一下是如何解析init.rc下面,on的关键字及其对应的action与command的。

按照惯例,先来看一下这个函数实现:

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;}act = calloc(1, sizeof(*act)); //初始化结构体actionact->name = args[1]; // 对action的name进行赋值list_init(&act->commands); // 初始化action的commands这条结构体的内部链表list_init(&act->qlist); // 初始化qlist这条结构提内部的链表list_add_tail(&action_list, &act->alist); // 利用listnode alist,将当前的这个结构提加入到了以action_list为哨兵节点的链表中,/* XXX add to hash */return act;}这里初始化的内容全部都是链表的操作。为了更好的理解,我们先来看看action的机构体:struct action {/* node in list of all actions */struct listnode alist; // 使用这个listnode将其加入action_list/* 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;};好的,总结一下,在经过parse_action之后,当前的action会被加入到action_list为哨兵节点的链表中。

并且被初始化了commands以及qlist这两条结构体内部的链表。

在parse_new_section中,我们看到,在初始化完之后,会将当前state的parse_line置为parse_line_action

case K_on:state->context = parse_action(state, nargs, args);if (state->context) {state->parse_line = parse_line_action;return;}break;在执行接下来的action的command的时候,就会去执行parse_line_action()的函数.

详情如下:

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);(实质是parse_line_action(&state, nargs, args))}}那接下来就要看parse_line_action的实现了:static void parse_line_action(struct parse_state* state, int nargs, char **args){struct command *cmd;struct action *act = state->context; // 通过state->context,得到了刚才正在解析的actionint (*func)(int nargs, char **args);int kw, n;if (nargs == 0) { // 判断是否为空,如果没有要执行的command的话,就会返回return;}kw = lookup_keyword(args[0]); // 得到kw,原理与得到SECTION的一致if (!kw_is(kw, COMMAND)) { // 如果这个命令不是一个command的话,则返回error。parse_error(state, "invalid command '%s'\n", args[0]);return;}// 因为action下面,只执行command,而且这些command是已经定义在keywords里面的n = kw_nargs(kw); // 从keywords里面得到这个command需要几个参数,是在初始化数组的第三项,nargsif (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); // 对action的结构体中的command结构体进行初始化cmd->func = kw_func(kw); // 得到这个command需要执行的函数,并将其放在了func的这个指针里面cmd->line = state->line; // 得到这个command是在文件中的那一行cmd->filename = state->filename; // 是哪个文件中的commandscmd->nargs = nargs; // 这个commands的参数有几个memcpy(cmd->args, args, sizeof(char*) * nargs); // 将这几个参数都copy到commands的数组里面list_add_tail(&act->commands, &cmd->clist); // 将当前要执行的commands,加入到action的结构体中,listnode为commands的链表中}

那这边有个疑问了,action的结构体中,初始化了一个qlist,但是好像没有使用啊?

这个是为什么呢?

我们接着回到init.c的main函数中看一下:

init_parse_config_file("/init.rc");ERROR("action for each trigger <==== chao");action_for_each_trigger("early-init", action_add_queue_tail);在解析完这个init.rc文件之后,会去执行action_for_each_trigger函数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(所有的action)里面,进行遍历。act = node_to_item(node, struct action, alist);if (!strcmp(act->name, trigger)) { // 如果名字和“trigger”有相同的话,会去执行action_add_queue_tail函数func(act); // 实质执行的是action_add_queue_tail(act)函数}}}

我们接着来看看这个函数中来做的是什么:

void action_add_queue_tail(struct action *act){if (list_empty(&act->qlist)) {list_add_tail(&action_queue, &act->qlist);}}因为每一个action都被初始化了qlist,所以,这里肯定不是空。

而会将这个action结构体,又加入到了action_queue的链表中。这下就奇怪了,为什么一个action要加入到两个链表中呢?

一直觉得人应该去旅行,在年轻的时候,趁着有脾气装潇洒,

Android启动流程分析(八) 解析init.rc的action

相关文章:

你感兴趣的文章:

标签云: