#############################################
本文为极度寒冰原创,转载请注明出处
#############################################
在分析完解析init.rc的action之后,剩下的一部分就是解析service了。
而解析service还是需要回到parse_config里面来。根据前面的知识,我们也可以很容易的知道在关键字为section的时候,会进入到parse_new_section。
这里会先执行parse_service,然后将service以及后面跟的option设置为执行parse_line:parse_line_service。
要理解service的解析流程的话,首先要关注的就是service的结构体。
struct service { /* list of all services */ struct listnode slist; // listnode slist const char *name; // name const char *classname; // 默认值为defult unsigned flags; // 选项 pid_t pid; // Service所在进程的PID time_t time_started; /* time of last start */ time_t time_crashed; /* first crash within inspection window */ int nr_crashed; /* number of times crashed within window */ uid_t uid; // effective user ID gid_t gid; // effective group ID gid_t supp_gids[NR_SVC_SUPP_GIDS]; // supplementary ids size_t nr_supp_gids; // supp_gids的大小 char *seclabel; struct socketinfo *sockets; // 为service创建的Sockets struct svcenvinfo *envvars; // 为service设置的环境变量 struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1];}; /* ^-------'args' MUST be at the end of this struct! */
这个结构体相比较而言就比较简单了,除了service的本身属性以外,对于数据结构方面就只有一个listnode。
struct listnode slist;这也就是说,在service的结构体中,这个结构体只会被加入一条链表而不是像action的两条链表。
另外需要注意的是,在service的结构体中,也维护了一个action的结构体,这就是说,在service中,也存在着一个action的commands的链表?
然后我们就来看看parse_service的函数
static void *parse_service(struct parse_state *state, int nargs, char **args){ struct service *svc; // 声明结构体 if (nargs < 3) { // 如果service的参数小于三个的话,我们会认为service是个不正常的service。service最少的nargs也是3,分别为service关键字,service的名字,service启动的时候要执行的命令 parse_error(state, "services must have a name and a program\n"); return 0; } if (!valid_name(args[1])) { //如果service的name为不标准的名字的话,含有其它的符号的话,我们会认为这个service是不规范的service。 parse_error(state, "invalid service name '%s'\n", args[1]); return 0; } svc = service_find_by_name(args[1]); 。。// 会从已经存在的service_list里面去查找,是否已经有同名的service存在 if (svc) { // 如果发现有同名的service存在的话,则会返回error parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); return 0; } nargs -= 2; // 去除service关键字与service的name svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); // malloc这个结构体 if (!svc) { // 如果malloc失败的话,提示out of memory parse_error(state, "out of memory\n"); return 0; } svc->name = args[1]; // 设置service的name为service关键字后的第一个参数 svc->classname = "default"; // 默认的classname为default memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 将args剩余的参数复制到svc的args里面 svc->args[nargs] = 0; // 给args的最后一项设置为0 svc->nargs = nargs; // 参数的数目等于传进来的参数的数目 svc->onrestart.name = "onrestart"; // 设置onrestart.name为onrestart list_init(&svc->onrestart.commands); // 初始化onrestart的链表 list_add_tail(&service_list, &svc->slist); // 将当前的service结构体加入到了service_list的链表里面 return svc;}从上面我们知道,在执行完parse_service之后,会初始化service的一些属性,将service servicename之后的做为args进行保存。
另外,将这个解析出来的service加入到service_list的链表里面。
然后接下来,去执行的就是
state->parse_line = parse_line_service;那我们像action,再来看看parse_line_service的操作
static void parse_line_service(struct parse_state *state, int nargs, char **args){ struct service *svc = state->context; struct command *cmd; int i, kw, kw_nargs; if (nargs == 0) { return; } svc->ioprio_class = IoSchedClass_NONE; kw = lookup_keyword(args[0]); switch (kw) { case K_capability: break; case K_class: if (nargs != 2) { parse_error(state, "class option requires a classname\n"); } else { svc->classname = args[1]; } break; case K_console: svc->flags |= SVC_CONSOLE; break; case K_disabled: svc->flags |= SVC_DISABLED; svc->flags |= SVC_RC_DISABLED; break; case K_ioprio: if (nargs != 3) { parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n"); } else { 。。。。 ........ case K_onrestart: nargs--; args++; kw = lookup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { parse_error(state, "invalid command '%s'\n", args[0]); break; } kw_nargs = kw_nargs(kw); if (nargs < kw_nargs) { parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, kw_nargs > 2 ? "arguments" : "argument"); break; } cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); cmd->func = kw_func(kw); cmd->nargs = nargs; memcpy(cmd->args, args, sizeof(char*) * nargs); list_add_tail(&svc->onrestart.commands, &cmd->clist); break; ............... }
可以看到,parse_line_service的这个函数,主要就是将解析service的每一行,将其对应进不同的case里面,进行service结构体的填充。
可能这样讲,理解起来会有困难。
但是为了方便理解,我们从init.rc里面找个例子出来分析:
service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm在parse_line_service的时候,会在
class的关键字的时候,执行到
case K_class: if (nargs != 2) { // 判断是否是两个token,如果不是的话,格式错误 parse_error(state, "class option requires a classname\n"); } else { svc->classname = args[1]; } break;也就是将service的classname从"default"修改为“args[1]”
在user system的的option的时候,
会去执行
case K_user: if (nargs != 2) { parse_error(state, "user option requires a user id\n"); } else { svc->uid = decode_uid(args[1]); } break;会将uid设置为system对应的uid
另外需要注意的是,在解析的过程中,会有一个比较重要的option是restart,来看一下当执行到这个关键字的时候的时候,会运行什么。
case K_onrestart: nargs--; args++; kw = lookup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { // 判断是否是command,如果restart之后跟的不是command的话,就会返回error parse_error(state, "invalid command '%s'\n", args[0]); break; } kw_nargs = kw_nargs(kw); // 获得当前command所需的参数 if (nargs < kw_nargs) { // 如果传递的参数小于我们需要的参数的话,会返回error parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, kw_nargs > 2 ? "arguments" : "argument"); break; } cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); // 初始化command cmd->func = kw_func(kw); // 将kw所包含的func赋值给cmd->func cmd->nargs = nargs; // 将参数的个数保存为nargs memcpy(cmd->args, args, sizeof(char*) * nargs); // 将这些参数复制到cmd的args中 list_add_tail(&svc->onrestart.commands, &cmd->clist); // 将这些command加入到service结构体的内部链表中 break;
至此,我们分析了所有关于init.rc的解析问题。
在service的解析后,会生成一条链表保存service的结构体,然后service的结构体里面自己运行维护一个action。
这个action会包含所有的restatt包含的内容,也就是restart的option关键字后会包含要执行的command
这个的结构应该比action的要简单和明了的多。
分析完了解析,我们应该去看一下android是如何在启动的过程中去执行这些action和service的。