- 3.5.2 执行Action
- define kw_func(kw)(keyword_info[kw].func)
- define KEYWORD(symbol, flags, nargs, func)\
- include"keywords.h"
- undef KEYWORD
- define kw_func(kw)(keyword_info[kw].func)
- ifndef KEYWORD
- defineMAKE_KEYWORD_ENUM
- define KEYWORD(symbol, flags, nargs, func)K_##symbol,
- endif
- ifdefMAKE_KEYWORD_ENUM
- undefMAKE_KEYWORD_ENUM
- undef KEYWORD
- endif
3.5.2 执行Action
上一节分析了Action的触发,那么Action又是在哪里被执行的呢?定位到execute_one_command函数,其位于init.c中,代码如下:
void execute_one_command(void)
{
int ret;
/从Action中取出Command/
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;
cur_command=get_first_command(cur_action);
}else{
cur_command=get_next_command(cur_action, cur_command);
}
/调用Command中定义的func函数,执行Command/
ret=cur_command->func(cur_command->nargs, cur_command->args);
}
execute_one_command函数做了两部分工作:取命令和执行命令的func函数。这里的func便是command结构体中的成员函数func,这个函数是在parse_line_action解析Action的时候赋值的,代码如下:
cmd->func=kw_func(kw);
接下来定位到kw_func,分析这里都赋值了哪些函数。kw_func位于init_parser.c中,其代码如下:
define kw_func(kw)(keyword_info[kw].func)
这是个宏定义,需要找到keyword_info的定义。keyword_info位于init_parser.c中,其代码如下:
define KEYWORD(symbol, flags, nargs, func)\
[K_##symbol]={#symbol, func, nargs+1,flags,},
struct{
const char*name;
int(func)(int nargs, char*args);
unsigned char nargs;
unsigned char flags;
}keyword_info[KEYWORD_COUNT]={
[K_UNKNOWN]={"unknown",0,0,0},
include"keywords.h"
};
undef KEYWORD
define kw_func(kw)(keyword_info[kw].func)
keyword_info中包含了keywords.h头文件,其代码如下:
ifndef KEYWORD
int do_chroot(int nargs, char**args);
int do_chdir(int nargs, char**args);
int do_class_start(int nargs, char**args);
int do_class_stop(int nargs, char**args);
//省略部分函数
defineMAKE_KEYWORD_ENUM
define KEYWORD(symbol, flags, nargs, func)K_##symbol,
enum{
K_UNKNOWN,
endif
KEYWORD(capability, OPTION,0,0)
KEYWORD(chdir, COMMAND,1,do_chdir)
KEYWORD(chroot, COMMAND,1,do_chroot)
KEYWORD(class, OPTION,0,0)
//省略部分KEYWORD
KEYWORD(exec, COMMAND,1,do_exec)
KEYWORD(on, SECTION,0,0)
KEYWORD(oneshot, OPTION,0,0)
KEYWORD(onrestart, OPTION,0,0)
//省略部分KEYWORD
KEYWORD(ioprio, OPTION,0,0)
ifdefMAKE_KEYWORD_ENUM
KEYWORD_COUNT,
};
undefMAKE_KEYWORD_ENUM
undef KEYWORD
endif
这里定义了所有Command对应的执行函数,执行Command就是执行这些函数。接下来,分别讲解Action和Service是如何执行的。
这里以init.rc中定义的early-init Action为例讲解Action的执行过程。early-init的定义如下:
on early-init
write/proc/1/oom_adj-16
start ueventd
write和start都是Command,在KEYWORD映射表中分别对应可执行函数do_write和do_start。
do_write定义在builtins.c中,定位到do_write函数体,其代码如下:
int do_write(int nargs, char**args)
{
const char*path=args[1];
const char*value=args[2];
……//省略部分内容
/write_file最终调用了open、write、close库函数往path里写入value/
return write_file(path, value);
}
write命令最终调用了基本的函数库,写入命令指定的参数。
接下来定位到do_start函数体,代码如下:
int do_start(int nargs, char**args)
{
struct service*svc;
//do_start是用于启动Service的
svc=service_find_by_name(args[1]);
if(svc){
/启动Service,这个Service就是ueventd/
service_start(svc, NULL);}
return 0;
}
找到了service_start,距离真相就不远了。下面继续分析service_start函数,其位于init.c中,代码如下:
void service_start(struct servicesvc, const chardynamic_args)
{
struct stat s;
pid_t pid;
int needs_console;
int n;
//Service启动前需要清除异常状态
svc->flags&=(~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
svc->time_started=0;
……//省略部分内容
/*调用fork创建子进程,fork函数调用一次,但会返回两次,分别返回子进程和父进程。其中
返回0表示在子进程中;返回大于0的数字表示在父进程中,这个大于0的数字便是子进程的进程ID/
pid=fork();
if(pid==0){//返回0,表示在子进程中
struct socketinfo*si;
struct svcenvinfo*ei;
char tmp[32];
int fd, sz;
/将属性信息添加到环境变量中/
if(properties_inited()){
get_property_workspace(&fd,&sz);
add_environment("ANDROID_PROPERTY_WORKSPACE",tmp);
}
for(ei=svc->envvars;ei;ei=ei->next)
add_environment(ei->name, ei->value);
/创建Socket,并在环境变量中设置Socket信息/
for(si=svc->sockets;si;si=si->next){
int socket_type=(
!strcmp(si->type,"stream")?SOCK_STREAM:
(!strcmp(si->type,"dgram")?SOCK_DGRAM:SOCK_SEQPACKET));
int s=create_socket(si->name, socket_type,
si->perm, si->uid, si->gid);
if(s>=0){
publish_socket(si->name, s);
}
}
……//省略部分内容
/根据参数值,调用Linux系统函数execve执行Service对应的命令,这里是ueventd/
if(!dynamic_args){
if(execve(svc->args[0],(char)svc->args,(char)ENV)<0){
ERROR("cannot execve('%s'):%s\n",svc->args[0],
strerror(errno));
}
}else{
……//省略部分内容
}
/以下是父进程,设置了Service的启动信息,并更新Service的属性状态,属性系统下节介绍/
svc->time_started=gettime();
svc->pid=pid;
svc->flags|=SVC_RUNNING;
if(properties_inited())
notify_service_state(svc->name,"running");
}
到这里early-init这个Action就分析完了。从这个Action可以看出,有一部分Command是以Service的方式执行的,这部分Service并不是以service关键字显式声明的。那么显式声明的Service又是如何启动的呢?