您的位置:首页 > 编程语言

深入理解init_3 --------- 解析Zygote 的service(基于源码2.2,代码源自Google)

2017-03-02 15:56 591 查看

深入理解init_3 ——— 解析Zygote 的service

1、解析service

zygote对应的service section 内容是:



解析zygo service 的主要函数


1.1文件位置 init.rc

/system/core/rootdir/init.rc

1.2、关键代码分析

service zygote  /system/bin/app_process -Xzygote /system/bin -zygote \
---start -system-server
socket zygote stream 666  #socket 是OPTION
#下面的onreatart 是OPTION ,而write和restart是COMMAND
onrestart write /sys/android_power/request_state wake
onrestart  write /sys/power/state on
onrestart restart media


解析section的入口函数是parse_new_section,它的代码如下:

2、跳入parser.c文件解析section

2.1、文件位置

/system/core/init/parser.c

2.2、主要代码分析

void parse_new_section(struct parser_state *state,int kw,int nargs,char **args)

{
switch (kw){

case k_service : //用parse_service 和parse_line_service解析service
state->context = parse_service(state,narge,args);
If (state->context){
state->parse_line = parse_line_service;
return;
}
break;
case k_on;  //解析on section
(部分代码省略····)
break;
}
state->parse_line =parse_line_no_op;
}


在解析service的时候,我们看到用parse_service和parse_line_service这两个函数,在分别介绍他们之前,我们先看一下init是如何组织这个service的。

3、Init中 service的结构体介绍

Init中,使用了一个service的结构体来保存与service section相关信息,下面来看看这个结构体。

3.1、文件位置

/system/core/init/init.h

3.2、关键代码分析

struct service {
/*  listnode是一个特殊的结构体,在内核代码中用的最多,主要是用来将结构体链接成一个双向链表。Init 中有一个全局的service_list,专门用于保存解析配置文件后得到的service*/
struct listnode slist;
const char *name; //server 的名字,与我们对应的就是“zygote”
const char *classname; //service 所属class的名字,默认就是defult。
unsigned flags; //service的属性
pid_t pid; //进程号
time_t time_started; //上一次启动的时间
time_t time_crashed; //上一次死亡的时间
int nr_crashed;  //死亡次数
uid_t uid;  //uid,gid相关
gid_t gid;
gid_t supp_gids[NR_SUOO_GIDS];
size_t nr_supp_gids;
/*有些service需要使用socket,下面这个socketinfo用来描述socket的相关情况。我们的zygote也使用了socket,配置文件中的内容是socket zygote stream 666 它表示将创建一个AF_STREAM类型的socket(其实就是TCP_SOCKET),该socket的名为“zygote”,读写权限666*/
struct socketinfo *sockets;
//service 一般运行在单独的进程中,envvars用来描述创建这个进程时所需要的环境变量信息。
struct svcenvinfo *envvars;
/*虽然关键字onrestart标识一个OPTION,可是这个OPTION 后面一般跟着COMMAND,下面这个action结构体用来存储command信息*/
struct action onreatart;

//与keychord相关的内容

Int *keycodes;
Int nkeycodes;
Int keychord_id;

//io 优先级设置
Int iopro_class;
Int iopro_pri;
//参数个数
Int nargs;
//用于存储参数
char *args[1];

};


我们现在了解了service的结构体,相对来说还是比较清晰的。而zygote中的那三个onrestart 该怎么表示呢,下面我们看action这个结构体。


struct action {

/*一个action结构体可以存放在三个双向链表中,其中alist用于存储所有的action,qlist用于链接那些等待执行的action,tlist用于链接那些待某些条件满足后才会执行的action*/
}

struct listnode alist;
struct listnode qlist;
struct listnode tlist;

unsigned hash;
const char *name;

//这个OPTION对应的};
COMMAND  链表,以zygote为例,它有三个 onrestart option,所以它会创建三个command结构体。
struct listnode commands;
struct command *current;


4、跳转到parser.c文件

4.1、文件位置

/system/core/init/parser.c

4.2、关键代码分析

static void *parse_service(strust parse_state *state,int nargs,char **args)
{
struct service *svc;    //声明一个service结构体
(部分代码省略···)
//init维护了一个全局的service链表,先判断是否有同名的service 了
svc = service_find_by_name(args[1]);
if (svc){
(部分代码省略···)//如果有同名的service,则不能继续后面操作。
return 0;
}
nargs =2;
svc = calloc(1,sizeof(*svc)+sizeof(char*)*nargs);
(部分代码省略···)
svc->name=args[1];
svc->classname=”default” ; //设置classname为“default”,这个很关键
memcpy(svc->args,args+2,sizeof(char*)*nargs);
svc->args[na
4000
rgs]=0;
svc->nargs=nargs;
svc->onrestart.name=”onrestart”;

list_init(&svc->onrestart.commands);
//把zygote这个service加到全局链表service_list中。
list_add_tail(&service_list,&svc->slist);
return svc;
}


parse_service函数只是搭建了一个service的架子,具体的内容尚需由后面的解析函数来填充,下面来看看service的另一解析函数parse_line_service.

static void parse_line_service(strust parse_state *state,int nargs,char **args)
{
struct service *svc = state->context;
struct command *cmd;
int i,kw,kw_nargs;
(部分代码省略······)
svc->ioprio_class = IoSchedClass_NONE;
//其实还是根据关键字来做各种处理
kw = lookup_keyword(args[0]);
switch(kw){
case k_capability:
break;
case k_class:
if (nargs !=2){
(部分代码省略······)
}else{

svc->classname = args[1];
}
break;
(部分代码省略······)
case k_oneshot:
/*这是一个service的属性,一共有五个属性,分别为:
SVC_DISABLED:不随classs自动启动。下面将看到class作用。
SVC_ONESHOT: 退出后不需要重启,也就是说这个service只启动一次就可以了。
SVC_RUNNING:正在运行,这是service的状态。
SVC_RESTARING:等待在重启,也是service的状态
SVC_CONSOLE:该service需要控制台
SVC_CRITICAL:如果在规定时间内该service不断重启,则系统会重启并进入恢复模式。
zygote没有使用任何属性,这表明它随着class的处理自动启动。
退出后会由init重启,不使用控制台,即使不断重启也不会导致系统进入恢复模式。
*/
svc->flags |= SVC_ONESHOT;
break;
case k_onrestart:  //根据onrestart的内容,填充action结构体的内容。
nargs--;
args++;
kw = lookup_keyword(args[0]);
(部分代码省略······)
//创建command 结构体
cmd = malloc(sizeof(*cmd)+sizeof(char*)*nargs);
cmd->func = kw_func(kw);
cmd->nargs =nargs;
memcpy(cmd->args,args,sizeof(char*)*nargs);
//把新建的command加入到双向链表
list_add_tail(&svc->onrestart.commands,&cmd->clist);
break;
(部分代码省略······)
case k_socket:{ //创建socket相关信息
struct socketinfo *si;
(部分代码省略······)
si = calloc(1,sizeof(*si));
if (!si){

parse_error(sr=state,”out of memory\n”);
break;
}
si->name = args[1]; //socket 的名字
si->type =args[2]; //socket的类型
si->perm = strtoul(args[3],0,0); //socket的 读写权限
if (nargs > 4)
si -> gid = decode_uid (args[4]);
if (nargs > 5)
si -> gid = decode_uid(args[5]);
si->next = svc ->sockets;
svc ->sockets = si;
break;
}
(部分代码省略······)
default;
parse_error(state,”invalid option ‘%s’\n”,args[0]);
}
}


parse_line_service 将根据配置文件的内容填充service结构体,那么zygote解析完成后会得到什么呢?下图表明zygote解析后的结果:



从上图可知:

1) service_list 链表将解析后的service全部链接到了一起,并且是双向链表,前向节点用prev表示,后向节点用next表示。

2) socketinfo也是一个双向列表,因为zygote只有一个socket,所以用一个圆框socket表示链表的示范 。

3) onrestart通过command指向command链表,zygote有三个commands。

现在zygote这个service解析完了,接下来我们将会了解init 如何控制service。

文档参考:

整理抄录自 — 《深入理解Android卷1》,邓凡平著。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  代码分析