3.5 触发并启动Action和Service

init解析init.rc后,生成了存放Service和Action的链表。那么init又是如何控制这些Action和Service的呢?本节将详细分析这部分内容。

3.5.1 触发Action

init解析完init.rc后,接着执行了action_for_each_trigger和queue_builtin_action。这两个函数做了些什么呢?

首先定位到action_for_each_trigger,其实现代码位于init_parser.c中,代码如下:


void action_for_each_trigger(const chartrigger, void(func)(struct action*act))

{

struct listnode*node;

struct action*act;

/一个怪异的函数调用,特别是node_to_item的第二个参数/

list_for_each(node,&action_list){

act=node_to_item(node, struct action, alist);

if(!strcmp(act->name, trigger)){

func(act);//执行了传入的func函数

}

}

}


list_for_each和node_to_item到底做了些什么?node_to_item第二个参数struct action又是什么?这两部分定义在list.h中,其代码如下:


define list_for_each(node, list)\

for(node=(list)->next;node!=(list);node=node->next)


原来list_for_each是一个宏,代表一个for循环。node_to_item的代码如下:


define node_to_item(node, container, member)\

(container)(((char)(node))-offsetof(container, member))


node_to_item又是一个宏,第二个参数接受一个container标识的参数,这个参数将由一个数据类型替换,所以才能在代码中直接传入类型struct action。

这里涉及C语言中一个非常关键的宏定义:offsetof。这个宏利用了结构体中成员偏移量是固定的这个特性,用于求结构体中某个成员在该结构体中的偏移量。其定义在/bionic/libc/kernel/common/linux/stddef. h文件中,代码如下:


ifdef__compiler_offsetof

define offsetof(TYPE, MEMBER)__compiler_offsetof(TYPE, MEMBER)

else

define offsetof(TYPE, MEMBER)((size_t)&((TYPE*)0)->MEMBER)

endif


下面详细分析这个宏定义。

(TYPE)0是将0强制转换为TYPE型指针。告诉编译器有一个指向TYPE类型的指针,这个指针的地址值是0。当然这都是欺骗编译器的,因为不需要操作这个0地址,不会出错。如果定义ptr=(TYPE)0,ptr是指向TYPE类型的指针,它的基地址值就是0。那么ptr->MEMBER就是MEMBER这个元素了,&(ptr->MEMBER)就是MENBER的地址。既然基地址为0,这样MEMBER的地址便是MEMBER在TYPE中的偏移量。最后把结果强制转换为size_t(size_t其实是unsigned int)就得到了MEMBER的偏移量。分析完了offsetof,再回到action_for_each_trigger函数。将node_to_item(node, struct action, alist)替换为如下代码:


(struct action)(((char)(node))-offsetof(struct action, alist))


(char)(node)是按照char格式读取node的值,node中便是alist的地址。然后将offsetof(struct action, alist)替换为如下代码:


((size_t)&((struct action*)0)->alist)


这里得到了alist在action中的偏移量。(((char)(node))-offsetof(struct action, alist))便得到了这个node对应的Action的地址,最后告诉编译器以(struct action)格式读取这个地址,这样便得到了node所在的Action,找到了node对应的数据。

接下来分析action_add_queue_tail中做了什么。代码如下:


void action_add_queue_tail(struct action*act)

{

list_add_tail(&action_queue,&act->qlist);

}


action_add_queue_tail中只是把Action中的qlist放入了action_queue中。找到action_queue的声明,发现它与service_list和action_list一样,都是由list_declare声明的宏。代码如下:


static list_declare(action_queue);


queue_builtin_action的执行过程与action_for_each_trigger类似,最后也是调用了action_add_queue_tail和list_add_tail方法,这里不再具体分析。

看来action_for_each_trigger和queue_builtin_action都没有实际执行Service和Action。