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-init

start ueventd

service ueventd /sbin/ueventd
class core
critical

import 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为空。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。