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又是如何启动的呢?