3.2.2 解析service
最后更新于:2022-04-02 05:48:43
zygote对应的service section内容是:
**init.rc::zygote**
~~~
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \ --start-system-server
socketzygote stream 666 #socket是OPTION
#下面的onrestart是OPTION,而write和restart是COMMAND
onrestartwrite /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestartrestart media
~~~
解析section的入口函数是parse_new_section,它的代码如下所示:
**parser.c**
~~~
void parse_new_section(struct parse_state*state, int kw,
int nargs, char **args)
{
switch(kw) {
caseK_service: //解析service,用parse_service和parse_line_service
state->context = parse_service(state, nargs, args);
if(state->context) {
state->parse_line = parse_line_service;
return;
}
break;
caseK_on: //解析on section
......//读者可以自己研究
break;
}
state->parse_line = parse_line_no_op;
}
~~~
其中,service解析时,用到了parse_service和parse_line_service两个函数,在分别介绍它们之前,先看init是如何组织这个service的。
1. service结构体
init中使用了一个叫service的结构体来保存和service section相关的信息,不妨来看这个结构体,代码如下所示:
**init.h::service结构体定义**
~~~
struct service {
//listnode是一个特殊的结构体,在内核代码中用得非常多,主要用来将结构体链接成一个
//双向链表。init中有一个全局的service_list,专门用来保存解析配置文件后得到的service。
struct listnode slist;
constchar *name; //service的名字,对应我们这个例子就是”zygote”。
constchar *classname; //service所属class的名字,默认是”defult”
unsigned flags;//service的属性
pid_tpid; //进程号
time_ttime_started; //上一次启动的时间
time_ttime_crashed; //上一次死亡的时间
intnr_crashed; //死亡次数
uid_tuid; //uid,gid相关
gid_tgid;
gid_tsupp_gids[NR_SVC_SUPP_GIDS];
size_tnr_supp_gids;
/*
有些service需要使用socket,下面这个socketinfo用来描述socket的相关信息。
我们的zygote也使用了socket,配置文件中的内容是socket zygote stream 666。
它表示将创建一个AF_STREAM类型的socket(其实就是TCP socket),该socket的名为“zygote”,
读写权限是666。
*/
structsocketinfo *sockets;
//service一般运行在单独的一个进程中,envvars用来描述创建这个进程时所需的环境变量信息。
structsvcenvinfo *envvars;
/*
虽然关键字onrestart标示一个OPTION,可是这个OPTION后面一般跟着COMMAND,
下面这个action结构体可用来存储command信息,马上就会分析到它。
*/
structaction onrestart;
//和keychord相关的内容
int*keycodes;
intnkeycodes;
intkeychord_id;
//io优先级设置
intioprio_class;
intioprio_pri;
//参数个数
intnargs;
//用于存储参数
char*args[1];
};
~~~
我们现在已了解的service的结构体,相对来说还算是清晰易懂的。而zygote中的那三个onrestart该怎么表示呢?请看service中使用的这个action结构体:
**init.h::action结构体定义**
~~~
struct action {
/*
一个action结构体可存放在三个双向链表中,其中alist用于存储所有action,
qlist用于链接那些等待执行的action,tlist用于链接那些待某些条件满足后
就需要执行的action。
*/
structlistnode alist;
structlistnode qlist;
structlistnode tlist;
unsigned hash;
constchar *name;
//这个OPTION对应的COMMAND链表,以zygote为例,它有三个onrestart option,所以
//它对应会创建三个command结构体。
structlistnode commands;
structcommand *current;
};
~~~
了解了上面的知识后,你是否能猜到parse_service和parse_line_service的作用了呢?马上就来看它们。
2. parse_service
parse_service的代码如下所示:
**parser.c**
~~~
static void *parse_service(struct parse_state*state, int nargs, char **args)
{
structservice *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[nargs] = 0;
svc->nargs = nargs;
svc->onrestart.name= "onrestart";
list_init(&svc->onrestart.commands);
//把zygote这个service加到全局链表service_list中。
list_add_tail(&service_list, &svc->slist);
returnsvc;
}
~~~
parse_service函数只是搭建了一个service的架子,具体的内容尚需由后面的解析函数来填充。来看service的另外一个解析函数parse_line_service。
3. parse_line_service
parse_line_service的代码如下所示:
**parser.c**
~~~
static void parse_line_service(structparse_state *state, int nargs,
char **args)
{
structservice *svc = state->context;
structcommand *cmd;
int i,kw, kw_nargs;
......
svc->ioprio_class = IoSchedClass_NONE;
//其实还是根据关键字来做各种处理。
kw =lookup_keyword(args[0]);
switch(kw) {
caseK_capability:
break;
caseK_class:
if(nargs != 2) {
......
}else {
svc->classname = args[1];
}
break;
......
caseK_oneshot:
/*
这是service的属性,它一共有五个属性,分别为:
SVC_DISABLED:不随class自动启动。下面将会看到class的作用。
SVC_ONESHOT:退出后不需要重启,也就是这个service只启动一次就可以了。
SVC_RUNNING:正在运行,这是service的状态。
SVC_RESTARTING:等待重启,这也是service的状态。
SVC_CONSOLE:该service需要使用控制台 。
SVC_CRITICAL:如果在规定时间内该service不断重启,则系统会重启并进入恢复模式。
zygote没有使用任何属性,这表明它:会随着class的处理自动启动;
退出后会由init重启;不使用控制台;即使不断重启也不会导致系统进入恢复模式。
*/
svc->flags |= SVC_ONESHOT;
break;
caseK_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;
......
caseK_socket: { //创建socket相关信息
struct socketinfo *si;
......
si= calloc(1, sizeof(*si));
if(!si) {
parse_error(state, "out of memory\n");
break;
}
si->name = args[1]; //socket的名字
si->type = args[2]; //socket的类型
si->perm = strtoul(args[3], 0, 8); //socket的读写权限
if(nargs > 4)
si->uid = 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解析完后会得到什么呢?图3-1表示了zygote解析后的结果:
:-: ![](http://img.blog.csdn.net/20150802095010545?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图3-1 zygote解析结果示意图
从上图中可知:
- service_list链表将解析后的service全部链接到了一起,并且是一个双向链表,前向节点用prev表示,后向节点用next表示。
- socketinfo也是一个双向链表,因为zygote只有一个socket,所以画了一个虚框socket做为链表的示范。
- onrestart通过commands指向一个commands链表,zygote有三个commands。
zygote这个service解析完了,现在就是“万事俱备,只欠东风”了。接下来要了解的是,init是如何控制service的。
';