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。