首页 > 代码库 > Android之init进程分析(1)

Android之init进程分析(1)

本文介绍init进程中的action触发方式

一,什么是action

在android中,使用action来管理并执行命令。action是一个数据结构,里面包含了命令集合command,action的名字等。Android系统通过action来执行一组命令。

struct action {/* node in list of all actions */struct listnode alist;/* 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;};

 所有的action都是通过alist组成一个链表(action_list),通过qlist组成一个执行队列(action_queue),tlist暂时没有用到。action里面的所有命令,以struct command的结构存放在commands为头的链表中;current指向当前的命令。

struct command{/* list of commands in an action */struct listnode clist;int (*func)(int nargs, char **args);int nargs;char *args[1];};

 上面的command结构体中,用clist来链接action下面的所有命令,命令包含了函数func,参数个数nargs,以及参数args。

有两个链表头,来链接action们:

static list_declare(action_list);static list_declare(action_queue);

 其中第一个为action链表头,第二个为action执行链表头。从init.rc配置文件中读出来的以及代码中设置的action,首先加入到action_list链表中,然后根据启动时机(init,boot阶段等)把需要执行的action加入到action_queue中。在init的主循环中,action_queue中的命令得到执行。

二,action是如何添加的

有两种方式把action添加到action_list和action_queue链表中,第一种为init.rc中配置,在init进程启动过程中解析init.rc配置文件;另外一种为代码中调用函数添加。

首先介绍第一种方式。 init进程启动时,会读取init.rc(以及init.xxxx,rc,xxxx为设备名字)配置文件。init.rc定义了专门的格式,分为三种类型:命令(action),服务(service),导入(import),比如:

on early-initstart ueventdservice ueventd /sbin/ueventdclass corecriticalimport init.test.rc

 如上,以on开头的标志了action开始,early-init为action的名字,下面的start ueventd为action的命令,可以有多条命令,直到遇到其他类型的定义关键字(这里是遇到service)为止。以service开头标志了服务,service后面的为服务名字和服务可执行程序,下面的class core等,为uevented服务的属性,可以有多条属性,直到遇到其他类型为止。import导入其他的文件。

在init进程中,先把配置文件读入到内存中 ,然后调用int init_parse_config_file(const char *fn)函数来解析配置文件,把on开头的读入到action结构体中,其后面的命令读入到command结构体中,并链接到action中。然后把action结构链接到action_list表头中。

之后通过调用action_for_each_trigger(“early-init”, action_add_queue_tail)等函数把名称为early-init的action加入到action_queue中。

除了解析配置文件构建action之外,代码中也可以通过调用queue_builtin_action来添加action到action_list和action_queue中:

void queue_builtin_action(int (*func)(int nargs, char **args), char *name){struct action *act;struct command *cmd;act = calloc(1, sizeof(*act));act->name = name;list_init(&act->commands);cmd = calloc(1, sizeof(*cmd));cmd->func = func;cmd->args[0] = name;list_add_tail(&act->commands, &cmd->clist);list_add_tail(&action_list, &act->alist);action_add_queue_tail(act);}void action_add_queue_tail(struct action *act){list_add_tail(&action_queue, &act->qlist);}

 函数申请了action和command的内存空间,然后链接到action_list和action_queue中。

三,action的执行

action_list里面包含了一系列的action,并且按照一定次序添加到了action_queue队列中。接下来需要从action_queue中读取action,并且依次执行里面的command。

init的主循环中,做了命令的执行动作:

for(;;) {int nr, i, timeout = -1;execute_one_command();restart_processes();……….if (!action_queue_empty() || cur_action)timeout = 0;……….nr = poll(ufds, fd_count, timeout);if (nr <= 0)continue;……….,}

 如上,execute_one_command调用只执行了一个command命令,如果执行完整个action的所有命令,则把此action从action_queue中删除。事实上,init进程开始的时候,加入了很多action到队列中,因此需要反复循环调用execute_one_command才能够执行完成。

通过action_queue_empty调用来控制循环,当action_queue不为空的时候,代码设置了timeout = 0,这样会导致poll(ufds, fd_count, timeout)调用立即返回,而不是以阻塞 方式等待。所以一旦有action加入到action_queue队列中,整个for循环就会轮回多次,多次执行execute_one_command,直到action_queue为空。

 

Android之init进程分析(1)