本文的大体流程还是按照书本上来,分三段。
(一)从Main开始到service start
由于本文内容较长,重新组织了下文章结构,将原文一分为三。
(一)从Main开始到service start.
这里提到的浅析,只是根据自己看代码的经验,在此记录下。
因为自己在网上查找相关信息的时候,发现内容不够详实,
有些还过于老旧(如自己所看的《深入理解Android 卷1》,里面就是基于Android 2.x的)。
最后发现,在Read the Fucking code的过程中,居然自己弄懂了,觉得有必要
在此记录,与大家分享下。
由于我看的代码版本比较新,自己也不知道从哪个版本开始,Android 的Init.c就被改成这样了。
这里姑且认为是Android 4.2及以后版本吧(可能前面的版本已经改成这样了)。
代码路径: system\core\init\init.c
需要理解的点:
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
In parse_config:
case T_NEWLINE:
state.line++;
if (nargs) {
int kw = lookup_keyword(args[0]);
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);
}
nargs = 0;
}
break;</span>
In parse_new_section
case K_on:
state->context = parse_action(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_action;
return;
}
break;
init_parse_config_file("/init.rc")
-----> parse_config
-----> T_NEWLINE , lookup_keyword , (kw_is(kw, SECTION)) ,
请参考keywords.h里面的:
KEYWORD(import, SECTION, 1, 0)
KEYWORD(on, SECTION, 0, 0)
KEYWORD(service, SECTION, 0, 0)
也就是说此处主要处理的是init.rc里面的import , on , service
------> parse_new_section 将on 所对应的action添加到action_list上,
将service对应的条目添加到service_list链表上。
还有一个action_queue,那个是在main函数(init.c)中通过action_for_each_trigger
将成员添加的。
------> 当处理每条section条目后,会调用parse_line对其后紧跟的非section行进行处理,直到
遇到新的section。此处,on对应的是parse_line_action, 而service对应的是parse_line_service
其实,主要是将那些行添加到双向链表上,以parse_line_service为例,请参考
case K_onrestart 里面的 list_add_tail(&svc->onrestart.commands, &cmd->clist);
之后通过 execute_one_command(); ( main函数的for循环,位于init.c中)执行各个action_queue
中各个action对应的命令。在main可看到,action_queue中的结点是通过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) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act->name, trigger)) {
func(act);
}
}
}
也就是说,如果action_list里面有此action,则调用func,其实就是action_add_queue_tail
添加到action_queue中。这里是通过strcmp进行字符串比较,而已经添加到action_list里面的
node是通过解析init.rc得到。
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
/* skip mounting filesystems in charger mode */
if (!is_charger) {
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", action_add_queue_tail);
action_for_each_trigger("post-fs-data", action_add_queue_tail);
}
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
}
/* run all property triggers based on current state of the properties */
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
这里为啥会跑出来个queue_builtin_action呢? 因为这里面所用的action不在init.rc中,
action_for_each_trigger不能找到action_list中对应的匹配,也就不能通过此函数将
此action添加到action_list中。但是由于这些action又是需要做的,所以单独写了个函数,
生成所需要的结点,然后直接将其加入action_list。
BTW,queue_builtin_action 有使用calloc函数,会看到有直接使用结构体里面的指针
成员,但没有看到此指针的初始化,这里是否会有问题呢??
这里有必要说一下C语言中malloc函数与calloc函数的区别:
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
看到了么? 那些指针被初始化为0 (NULL) 了。
通过代码,我们知道诸如early-init, init, early-boot, boot,希望加入到action_queue中。查看
init.rc,我们发现有on init, on early-init以及on boot, 所以init, early-init, boot是都有被添加到action-list的。
于是,上面3个也会被加入到action_queue。
我们再来看看execute_one_command到底做了什么。
void execute_one_command(void)
{
int ret;
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
cur_action = action_remove_queue_head();
cur_command = NULL;
if (!cur_action)
return;
INFO("processing action %p (%s)\n", cur_action, cur_action->name);
cur_command = get_first_command(cur_action);
} else {
cur_command = get_next_command(cur_action, cur_command);
}
if (!cur_command)
return;
ret = cur_command->func(cur_command->nargs, cur_command->args);
INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}
在此简析execute_one_command函数:
(1)循环第一次进入此函数时,cur_action 和cur_command均为NULL,于是会调用
action_remove_queue_head从action_queue中取出第一个action。然后调用get_first_command
获取此action的第一个command,至此,cur_action和cur_command均被赋值,不再为NULL了。
(2)循环第二次进入此函数时,cur_action 和cur_command均不为NULL,于是通过is_last_command
判断cur_command是否为cur_action最后一个command。如果不是,会调用get_next_command
去获取之前那个action的第二个command。否则,cur_action对应的各个command (command list里的)
均被处理了,那么将调用action_remove_queue_head从action_queue中取出第二个action。
(3)依此类推,action_queue里面的各个action所对应的的各个command均会被处理。
(我想这次分析得这么细致,应该所有看到的人都容易理解吧。)
struct action *action_remove_queue_head(void)
{
if (list_empty(&action_queue)) {
return 0;
} else {
struct listnode *node = list_head(&action_queue);
struct action *act = node_to_item(node, struct action, qlist);
list_remove(node);
list_init(node);
return act;
}
}
static struct command *get_first_command(struct action *act)
{
struct listnode *node;
node = list_head(&act->commands);
if (!node || list_empty(&act->commands))
return NULL;
return node_to_item(node, struct command, clist);
}
static struct command *get_next_command(struct action *act, struct command *cmd)
{
struct listnode *node;
node = cmd->clist.next;
if (!node)
return NULL;
if (node == &act->commands)
return NULL;
return node_to_item(node, struct command, clist);
}
初始值cur_action 和cur_command均为NULL。 由于这个是循环,所以,cur_action第一次
得到的是early-init,并依次执行该action下对应的各个command。 当然,boot这个action
下的command也会在处理boot这个action的时候被执行。
on boot那个section下面有class_start core
class_start main
于是,会执行class_start这个关键字对应的function, 也就是do_class_start 。
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
void service_for_each_class(const char *classname,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
<span style="color:#ff6666;">list_for_each</span>(node, &service_list) {
svc = node_to_item(node, struct service, slist);
if (!strcmp(svc->classname, classname)) {
func(svc);
}
}
}
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
service_start(svc, NULL);
}
}
而在service_start里面,我们可以看到时通过fork创建了子进程,
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); 启动子进程。
我们再看看init.rc中的一句:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server<br>
在parse_service里面有填充args成员,是读取到的init.rc内容行的arg+2,其实就是
/system/bin/app_process
就这样,init.rc中的各个service依次启动了。
总结下service start的流程是:
(1)init_parse_config_file("/init.rc") 读取并分析init.rc文件,将里面的
action和service分别添加到action_list 和 service_list
(2)调用action_for_each_trigger,queue_builtin_action,直接或间接调用
action_add_queue_tail 将action_list里面存在的action和单独指定的"builtin action”
添加到action queue。
(3)在for循环里面调用execute_one_command执行action_queue里面添加的
各个action里面的各个command,当然也包括"on" section对应的"boot "action。
(4)"boot "action 里面的class start 这个command的执行,其实对应的就是do_class_start
函数执行,最终service_start被调用来启动各个service。
之前也看了个文章,关于Android Init进程启动的,里面对Init.rc脚本中的各个部分做了概要性的介绍。
网址:http://www.cnblogs.com/mr-raptor/archive/2012/07/12/2588920.html