目录
- 目录
- init解析配置文件
- 关键字定义
- kw_is
- 解析
- K_import
- K_on
- command执行
- K_service
- service
- service结构体
- parse_service
- parse_line_service
- init控制service
init解析配置文件
在解析service服务是如何启动之前,让我们先来学习一下init进程是如何解析init.rc等配置文件的。
init进程解析配置文件的代码如下(/system/core/init/init.c&&/system/core/init/init_parser.c):
init_parse_config_file("/init.rc");void *read_file(const char *fn, unsigned *_sz){ char *data; int sz; int fd; struct stat sb; data = 0; fd = open(fn, O_RDONLY); if (fd < 0) return 0; // 获取init.rc文件字符总数 sz = lseek(fd, 0, SEEK_END); if (sz < 0) goto oops; if (lseek(fd, 0, SEEK_SET) != 0) goto oops; // 多余两个字符存储'\n'和'\0' data = (char*)malloc(sz + 2); if (data == 0) goto oops; // 读取init.rc文件内容到data数组中 if (read(fd, data, sz) != sz) goto oops; close(fd); data[sz] = '\n'; data[sz + 1] = 0; if(_sz) *_sz = sz; return data;oops: close(fd); if (data != 0) free(data); return 0;}int init_parse_config_file(const char *fn){ char *data; data = read_file(fn, 0); if (!data) return -1; parse_config(fn, data); DUMP(); return 0;}
读完init.rc文件内容到data数组中后,将调用parse_config函数进行解析,具体函数源码如下:
struct parse_state{ char *ptr; char *text; int line; int nexttoken; void *context; void (*parse_line)(struct parse_state *state, int nargs, char **args); const char *filename; void *priv;};#define T_EOF 0#define T_TEXT 1#define T_NEWLINE 2int next_token(struct parse_state *state){ char *x = state->ptr; char *s; if (state->nexttoken) { int t = state->nexttoken; state->nexttoken = 0; return t; } for (;;) { switch(*x) { case 0: state->ptr = x; return T_EOF; case '\n': x++; state->ptr = x; return T_NEWLINE; case ' ': case '\t': case '\r': x++; continue; case '#': while (*x && (*x != '\n')) x ++; if (*x == '\n') { state->ptr = x + 1; return T_NEWLINE; } else { state->ptr = x; return T_EOF; } default: goto text; } }text: state->text = s = x;}static void parse_config(const char *fn, char *s){ struct parse_state state; struct listnode import_list; struct listnode *node; char *args[INIT_PARSER_MAXARGS]; int nargs; nargs = 0; state.filename = fn; state.line = 0; state.ptr = s; state.nexttoken = 0; state.parse_line = parse_line_no_op; list_init(&import_list); state.priv = &import_list; for(;;) { switch(next_token(&state)) { case T_EOF: state.parse_line(&state, 0, 0); goto parser_done; case T_NEWLINE: state.line ++; if (nargs) { int kw = lookup_keyword(args[0]); if (ks_is(kw, SECTION)) { state.parse_line(&state, 0, 0); parse_new_section(&state, kw, nargs, args); } else { state.parse_line(&state, nargs, args); } nargs = 0; } break; case T_TEXT: if (nargs < INIT_PARSER_MAXARGS) { args[nargs ++] = state.text; } break; } }parser_done: // 解析import关键字导入的rc文件 list_for_each(node, &import_list) { struct import *import = node_to_item(node, struct import, list); int ret; INFO("importing '%s'", import->filename); ret = init_parse_config_file(import->filename); if (ret) { ERROR("could not import file '%s' from '%s'\n", import->filename, fn); } }}
上面就是parse_config函数的实现。函数通过调用next_token函数来解析data数组内容,当有新的一行有效数据时,则把有效数据存入到args字符指针数组中,然后在T_NEWLINE分支中进行真正的解析,这也是解析过程中最复杂的地方。让我们来深入的学习一下T_NEWLINE分支的解析过程。
关键字定义
我们先来看一下loopup_keyword函数的具体实现,源码位于/system/core/init/init_parser.c文件中,源码内容如下:
int lookup_keyword(const char *s){ switch(*s ++) { case 'c': if (!strcmp(s, "opy")) return K_copy; if (!strcmp(s, "apability")) return K_capability; if (!strcmp(s, "hdir")) return K_chdir; if (!strcmp(s, "hroot")) return K_chroot; if (!strcmp(s, "lass")) return K_class; if (!strcmp(s, "lass_start")) return K_class_start; if (!strcmp(s, "lass_stop")) return K_class_stop; if (!strcmp(s, "lass_reset")) return K_class_reset; if (!strcmp(s, "onsole")) return K_console; if (!strcmp(s, "hown")) return K_chown; if (!strcmp(s, "hmod")) return K_chmod; if (!strcmp(s, "ritical")) return K_critical; break; case 'd': if (!strcmp(s, "isabled")) return K_disabled; if (!strcmp(s, "omainname")) return K_domainname; break; case 'e': if (!strcmp(s, "xec")) return K_exec; if (!strcmp(s, "xport")) return K_export; break; case 'g': if (!strcmp(s, "roup")) return K_group; break; case 'h': if (!strcmp(s, "ostname")) return K_hostname; break; case 'i': if (!strcmp(s, "oprio")) return K_ioprio; if (!strcmp(s, "fup")) return K_ifup; if (!strcmp(s, "nsmod")) return K_insmod; if (!strcmp(s, "mport")) return K_import; break; case 'k': if (!strcmp(s, "eycodes")) return K_keycodes; break; case 'l': if (!strcmp(s, "oglevel")) return K_loglevel; if (!strcmp(s, "oad_persist_props")) return K_load_persist_props; break; case 'm': if (!strcmp(s, "kdir")) return K_mkdir; if (!strcmp(s, "ount_all")) return K_mount_all; if (!strcmp(s, "ount")) return K_mount; break; case 'o': if (!strcmp(s, "n")) return K_on; if (!strcmp(s, "neshot")) return K_oneshot; if (!strcmp(s, "nrestart")) return K_onrestart; break; case 'p': if (!strcmp(s, "owerctl")) return K_powerctl; break; case 'r': if (!strcmp(s, "estart")) return K_restart; if (!strcmp(s, "estorecon")) return K_restorecon; if (!strcmp(s, "mdir")) return K_rmdir; if (!strcmp(s, "m")) return K_rm; break; case 's': if (!strcmp(s, "eclabel")) return K_seclabel; if (!strcmp(s, "ervice")) return K_service; if (!strcmp(s, "etcon")) return K_setcon; if (!strcmp(s, "etenforce")) return K_setenforce; if (!strcmp(s, "etenv")) return K_setenv; if (!strcmp(s, "etkey")) return K_setkey; if (!strcmp(s, "etprop")) return K_setprop; if (!strcmp(s, "etrlimit")) return K_setrlimit; if (!strcmp(s, "etsebool")) return K_setsebool; if (!strcmp(s, "ocket")) return K_socket; if (!strcmp(s, "tart")) return K_start; if (!strcmp(s, "top")) return K_stop; if (!strcmp(s, "wapon_all")) return K_swapon_all; if (!strcmp(s, "ymlink")) return K_symlink; if (!strcmp(s, "ysclktz")) return K_sysclktz; break; case 't': if (!strcmp(s, "rigger")) return K_trigger; break; case 'u': if (!strcmp(s, "ser")) return K_user; break; case 'w': if (!strcmp(s, "rite")) return K_write; if (!strcmp(s, "ait")) return K_wait; break; } return K_UNKNOWN;}
lookup_keyword函数源码是非常简单的,罗列了一堆strcmp语句。而且源码在匹配case ‘p’的时候有一个明显的问题,大家感兴趣的可以自行查看Android源码。
接下来,判断关键字类型是不是SECTION,我们继续来看一下kw_is的源码。
kw_is
kw_is函数的源码实现如下:
#define SECTION 0x01#define kw_is(kw, type) (keyword_info[kw].flags & (type))
从上述代码中我们还不能搞清楚道理什么类型算是SECTION类型,这里直接告诉大家:只有当关键字为on、service和import的时候,关键字才被认为是SECTION
解析
虽然关键字的类型很多,但是大部分关键字对应的解析函数都是parse_line_no_op。而parse_line_no_op的实现却是空的,源码如下:
void parse_line_no_op(struct parse_state *state, int nargs, char **args){}
为什么会这样呢?
是因为,仔细分析最初的init_parse_config_file函数要解析的init.rc文件,虽然init.rc的内容很多,但是其中只有三种有效模式:
- 第一种trigger触发
on xxxx ......
- 第二种service
service servicename ......
- 第三种import
import *.rc
而这三种模式的关键字都是属于SECTION类型的,所以我们需要看一下parse_new_section函数里对这三种关键字的解析函数指针做了赋值。
parse_new_section函数的源码内容如下:
void parse_new_section(struct parse_state *state, int kw, int nargs, char **args){ printf("[ %s %s]\n", args[0], nargs > 1 ? args[1] : ""); switch(kw) { case K_service: state->context = parse_service(state, nargs, args); if (state->context) { state->parse_line = parse_line_service; return; } break; case K_on: state->context = parse_action(state, nargs, args); if (state->context) { state->parse_line = parse_line_action; return; } break; case K_import: parse_import(state, nargs, args); break; } state->parse_line = parse_line_no_op;}
果然,针对这三种关键字,其解析函数parse_line也被赋予了不同的值。我们挨个分析一下。
K_import
import模式主要是导入新的rc配置文件,所以我们看一下parse_import函数的具体实现:
struct import { struct listnode list; const char *filename;};void list_add_tail(struct listnode *head, struct listnode *item){ item->next = head; item->prev = head->prev; head->prev->next = item; head->prev = item;}void parse_import(struct parse_state *state, int nargs, char **args){ struct listnode *import_list = state->priv; struct import *import; char conf_file[PATH_MAX]; int ret; if (nargs != 2) { ERROR("single argument needed for import\n"); return; } ret = expand_props(conf_file, args[1], sizeof(conf_file)); if (ret) { ERROR("error while handing import on line '%d' in '%s'\n", state->line, state->filename); return; } import = calloc(1, sizeof(struct import)); import->filename = strdup(conf_file); list_add_tail(import_list, &import->list); INFO("found import '%s', adding to import list", import->filename);}
代码还是比较简单的,一句话概括就是将import导入的rc文件链接到state->priv链表中。state->priv链表链接了所有的import文件,最后会在parse_config函数的parser_done标签中进行处理。
K_on
K_on关键字代表了trigger模式,跟trigger模式相关的函数只要是parse_action和parse_line_action。
action结构体源码如下(/system/core/init/init.h):
struct action { /* node in list of all actions */ struct listnode alist; /* node in the queue of pending actions */ struct listnode qlist; /* node in the list of actions for a trigger */ struct listnode tlist; unsigned hash; const char *name; // command链表,以zygote为例,它有4个onrestart option,所以它对应会创建4个command结构体 struct listnode commands; struct command *current;};struct command{ /* list of commands in an action */ struct listnode clist; int (*func)(int nargs, char **args); int nargs; char *args[1];};
接下来,看一下parse_action和parse_line_action函数的具体实现。
void list_init(struct listnode *node){ node->next = node; node->prev = node;}void list_add_tail(struct listnode *head, struct listnode *item){ item->next = head; item->prev = head->prev; head->prev->next = item; head->prev = item;}static void *parse_action(struct parse_state *state, int nargs, char **args){ struct action *act; if (nargs < 2) { parse_error(state, "actions must have a trigger\n"); return 0; } if (nargs > 2) { parse_error(state, "actions may not have extra parameters\n"); return 0; } act = calloc(1, sizeof(*act)); act->name = args[1]; list_init(&act->commands); list_init(&act->qlist); list_add_tail(&action_list, &act->alist); return act;}static void parse_line_action(struct parse_state *state, int nargs, char **args){ struct command *cmd; struct action *act = state->context; int (*func)(int nargs, char **args); int kw, n; if (nargs == 0) { return; } kw = loopup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { parse_error(state, "invalid command '%s'\n", args[0]); return; } n = kw_nargs(kw); 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(&act->commands, &cmd->clist);}
在parse_line_action构造command结构体的时候,有一个关键函数kw_func,它返回函数指针决定了command的具体实现。我们来看一下kw_func的函数源码:
#define kw_nargs(kw) (keyword_info[kw].nargs)#define kw_func(kw) (keyword_info[kw].func)struct { const char *name; int (*func)(int nargs, char **args); unsigned char nargs; unsigned char flags;} keyword_info[KEYWORK_COUNT] = { [K_UNKNOWN] = {"unknown", 0, 0, 0}, #include "keywords.h"};
这keyword_info结构体的初始化是很神奇的,希望大家也能感受到它的精妙之处。可以看到,代码里初始化了一个成员K_UNKNOWN之后,后面跟了一句#include “keywords.h”。这就是神奇的地方,因为代码编译过程中,include引用的内容会在当前位置进行展开,也就是说keyword_info其它成员的初始化是在”keywords.h”文件中完成的。我们来看一下keywords.h文件源码():
#ifndef KEYWORDint 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);int do_class_reset(int nargs, char **args);int do_domainname(int nargs, char **args);int do_exec(int nargs, char **args);int do_export(int nargs, char **args);int do_hostname(int nargs, char **args);int do_ifup(int nargs, char **args);int do_insmod(int nargs, char **args);int do_mkdir(int nargs, char **args);int do_mount_all(int nargs, char **args);int do_mount(int nargs, char **args);int do_powerctl(int nargs, char **args);int do_restart(int nargs, char **args);int do_restorecon(int nargs, char **args);int do_rm(int nargs, char **args);int do_rmdir(int nargs, char **args);int do_setcon(int nargs, char **args);int do_setenforce(int nargs, char **args);int do_setkey(int nargs, char **args);int do_setprop(int nargs, char **args);int do_setrlimit(int nargs, char **args);int do_setsebool(int nargs, char **args);int do_start(int nargs, char **args);int do_stop(int nargs, char **args);int do_swapon_all(int nargs, char **args);int do_trigger(int nargs, char **args);int do_symlink(int nargs, char **args);int do_sysclktz(int nargs, char **args);int do_write(int nargs, char **args);int do_copy(int nargs, char **args);int do_chown(int nargs, char **args);int do_chmod(int nargs, char **args);int do_loglevel(int nargs, char **args);int do_load_persist_props(int nargs, char **args);int do_wait(int nargs, char **args);#define __MAKE_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(class_start, COMMAND, 1, do_class_start) KEYWORD(class_stop, COMMAND, 1, do_class_stop) KEYWORD(class_reset, COMMAND, 1, do_class_reset) KEYWORD(console, OPTION, 0, 0) KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) KEYWORD(domainname, COMMAND, 1, do_domainname) KEYWORD(exec, COMMAND, 1, do_exec) KEYWORD(export, COMMAND, 2, do_export) KEYWORD(group, OPTION, 0, 0) KEYWORD(hostname, COMMAND, 1, do_hostname) KEYWORD(ifup, COMMAND, 1, do_ifup) KEYWORD(insmod, COMMAND, 1, do_insmod) KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) KEYWORD(rm, COMMAND, 1, do_rm) KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(seclabel, OPTION, 0, 0) KEYWORD(service, SECTION, 0, 0) KEYWORD(setcon, COMMAND, 1, do_setcon) KEYWORD(setenforce, COMMAND, 1, do_setenforce) KEYWORD(setenv, OPTION, 2, 0) KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) KEYWORD(setsebool, COMMAND, 2, do_setsebool) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) KEYWORD(swapon_all, COMMAND, 1, do_swapon_all) KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) KEYWORD(ioprio, OPTION, 0, 0)#ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT,};#undef __MAKE_KEYWORD_ENUM__#undef KEYWORD#endif
因为KEYWORD已经在init_parser.c文件中定义了,所以从#ifndef KEYWORD到#endif之间的代码是不需要care的。KEYWORD的定义如下(/system/core/init/init_parser.c):
#define KEYWORD(symbol, flags, nargs, func) \ [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
这里结合KEYWORD(class_start, COMMAND, 1, do_class_start)来讲解一下这个宏的作用。
- 预处理运算符: #(单井号)——字符串化运算符。##(双井号)——连接运算符。
- K_##symbol == K_class_start。#symbol == class_start
- KEYWORD(class_start, COMMAND, 1, do_class_start) == [K_class_start] = {class_start, do_class_start, 2, COMMAND}
经过上述分析,我们经已经清楚了keyword_info数组的初始化过程。同时,也知道了相应的cmd的func函数指针指向的具体函数。func函数的具体实现在/system/core/init/builtins.c文件里,感兴趣的同学可以自行阅读源码。
command执行
那最终这些command是如何执行的呢?
因为on ×××是基于某个事件触发,init.rc中列举的事件包括:
- on early-init
- on init
- on fs
- on post-fs
- on post-fs-data
- on boot
- on nonencrypted
- on charger
- on property:key=value
那init.rc这些trigger是在什么时间点触发呢?其实这些trigger的触发顺序是由init.rc里面添加它们到action queue的顺序决定的。相关代码如下:
void action_for_each_trigger(const char *trigger, void (*func)(struct action *act)){ struct listnode *node; struct action *act; list_for_each(node, &action_list) { act = node_to_item(node, struct action, alist); if (!strcmp(act->name, trigger)) { func(act); } }}// func是函数指针,具体实现函数为action_add_queue_tailvoid action_add_queue_tail(struct action *act){ if (list_empty(&act->qlist)) { list_add_tail(&action_queue, &act->qlist); }}action_for_each_trigger("early-init", action_add_queue_tail);action_for_each_trigger("init", action_add_queue_tail);if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail);}if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail);} else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail);}
在init.c中以合适的顺序添加到action queue里面之后,在init.c main函数的最后会依次从action queue中取出这些action,顺序执行。
for(;;) { int nr, i, timeout = -1; execute_one_command();}
K_service
下文会重点介绍service的解析,这里就略过了。
service
上述重点在于SECTION类型中的K_on和K_import关键字解析。接下来,我们要看一下K_service关键字的解析实现了,以zygote service为例。
zygote对应的service section内容是(/system/core/rootdir/init.rc):
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd
解析service时,用到了parse_service和parse_line_service这两个函数,在分别介绍它们之前,我们先来学习一下service的数据结构。
service结构体
init进程中使用了一个叫做service的结构体来保存与service section相关的信息,结构体的定义如下(system/core/init/init.h):
struct service { /* list of all service */ struct listnode slist; const char *name; /* service的名字,比如"zygote" */ const char *classname; /* service所属的class的名字,默认是"default" */ unsigned flags; /* service的属性 */ pid_t 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; gid_t gid; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; char *seclabel; struct socketinfo *sockets; // service一般运行在一个独立的进程中,envvars用来描述创建这个进程时所需的环境变量信息 struct svcenvinfo *envvars; struct action onrestart; int *keycodes; int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; // 参数个数 char *args[1]; // 用于存储参数}
service一共有5个属性,分别为:
- SVC_DISABLED:不随class自动启动。
- SVC_ONESHOT:退出后不需要重启,也就是说这个service只启动一次就可以了。
- SVC_RUNNING:正在运行,这是service的状态。
- SVC_CONSOLE:该service需要使用控制台。
- SVC_CRITICAL:如果在规定的时间内该service不断重启,则系统会重启并进入恢复模式。
service的结构体虽然成员很多,但是比较好理解。但是,例如像zygote这种service,其中的onrestart该怎么表示呢?其实是用了action这个结构体。
了解了这些基本的结构体之后,我们接下来看一下parse_service和parse_line_service的具体实现。
parse_service
parse_service的源码位置是:/system/core/init/init_parser.c,源码如下:
static int valid_name(const char *name){ if (strlen(name) > 16) { return 0; } while (*name) { if (!isalnum(*name) && (*name != '_') && (*name != '-')) { return 0; } name++; } return 1;}#define list_for_each(node, list) \ for(node = (list)->next; node != (list); node = node->next)struct service *service_find_by_name(const char *name){ struct listnode *node; struct service *svc; list_for_each(node, &service_list) { svc = node_to_item(node, struct service, slist); if (!strcmp(svc->name, name)) { return svc; } } return 0;}static void *parse_service(struct parse_state *state, int nargs, char **args){ struct service *svc; if (nargs < 3) { parse_error(state, "services must have a name and a program\n"); return 0; } if (!valid_name(args[1])) { parse_error(state, "invalid service name '%s'\n", args[1]); return 0; } svc = service_find_by_name(args[1]); if (svc) { parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); return 0; } nargs -=2 ; svc = calloc(1, sizeof(*svc), sizeof(char*) * nargs); if (!svc) { parse_error(state, "out of memory\n"); return 0; } svc->name = args[1]; svc->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); list_add_tail(&service_list, &svc->slist);}
parse_service函数只是搭建了一个service的架子,具体的内容尚需由后面的解析函数来填充。接下来看一下另外一个解析函数parse_line_service。
parse_line_service
parse_line_service的源码位置:/system/core/init/init_parser.c,源码内容如下:
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 = loopup_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, "opprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n"); } else { svc->ioprio_pri = strtoul(args[2], 0, 8); if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) { parse_error(state, "priority value must be range 0 - 7\n"); break; } if (!strcmp(args[1], "rt")) { svc->ioprio_class = IoSchedClass_RT; } else if (!strcmp(args[1], "be")) { svc->ioprio_class = IoSchedClass_BE; } else if (!strcmp(args[1], "idle")) { svc->ioprio_class = IoSchedClass_IDLE; } else { parse_error(state, "ioprio option usage: ioprio <rt|be|dle> <0-7>\n"); } } break; case K_group: if (nargs < 2) { parse_error(state, "group option requires a group id\n"); } else if (nargs > NR_SVC_SUPP_GIDS + 2) { parse_error(state, "group option accepts at most %d supp. groups\n", NR_SVC_SUPP_GIDS); } else { int n; svc->gid = decode_uid(args[1]); for (n = 2; n < nargs; n ++) { svc->supp_gids[n-2] = decode_uid(args[n]); } svc->nr_supp_gids = n - 2; } break; case K_keycodes: if (nargs < 2) { parse_error(state, "keycodes option requires atleast one keycode\n"); } else { svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); if (!svc->keycodes) { parse_error(state, "could not allocate keycodes\n"); } else { svc->nkeycodes = nargs - 1; for (i = 1; i < nargs; i ++) { svc->keycodes[i - 1] = atoi(args[i]); } } } break; case K_oneshot: svc->flags |= SVC_ONESHOT; break; case K_onrestart: nagrs --; args ++; kw = loopup_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; case K_critical: svc->flags != SVC_CRITICAL; break; case K_setenv: struct svcenvinfo *ei; if (nargs < 2) { parse_error(state, "setenv option requires name and value arguments\n"); break; } ei = calloc(1, sizeof(*ei)); if (! ei) { parse_error(state, "out of memory\n"); break; } ei->name = args[1]; ei->value = args[2]; ei->next = svc->envvars; svc->envvars = ei; break; case K_socket: struct socketinfo *si; if (nargs < 4) { parse_error(state, "socket option requires name, type, perm arguments\n"); } if (strcmp(args[2], "dgram") && strcmp(args[2], "stream") && strcmp(args[2], "seqpacket")) { parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n"); break; } si = calloc(1, sizeof(*si)); if (!si) { parse_error(state, "out of memory\n"); break; } si->name = args[1]; si->type = args[2]; si->perm = strtoul(args[3], 0, 8); 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; case K_user: if (nargs != 2) { parse_error(state, "user option requires a user id\n"); } else { svc->uid = decode_uid(args[1]); } break; case K_seckable: if (nargs != 2) { parse_error(state, "seclable option requires a label string\n"); } else { svc->seclable = args[1]; } break; default: parse_error(state, "invalid option '%s'\n", args[0]); }}
parse_line_service将根据配置文件的内容填充service结构体,那么,zygote解析完后的结果为:
TODO:需要画图
init控制service
了解了service的数据结构和组装,接下来,我们需要了解init是如何控制service的。
init.rc中有这样一条命令:
class_start defaultclass_start mainclass_start core
class_start标识一个COMMAND,对应的处理函数为do_class_start,它位于on boot的范围内。当init进程执行到下面几句话时,do_class_start就会被执行了。
action_for_each_trigger("boot", action_add_queue_tail);
下面我们来看一下do_class_start函数,函数位置:/system/core/init/builtins.c:
int do_class_start(int nargs, char **args){ /** * Starting a class does not start services * which are explicitly disabled. They must * be started individually. */ service_for_each_class(args[1], service_start_if_not_disabled); return 0;}static void service_start_if_not_disabled(struct service *svc){ if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); }}
zygote这个service的classname值是main,flags标志位不是disable,所以会调用service_start来启动zygote。
service_start的函数实现如下(/system/core/init/init.c):
static const char *ENV[32];int add_environment(const char *key, const char *val){ int n; for (n = 0; n < 31; n ++) { if (!ENV[n]) { size_t len = strlen(key) + strlen(val) + 2; char *entry = malloc(len); snprintf(entry, len, "%s=%s", key, val); ENV[n] = entry; return 0; } }}#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"#define ANDROID_SOCKET_DIR "/dev/socket"static void publish_socket(const char *name, int fd){ char key[64] = ANDROID_SOCKET_ENV_PREFIX; char val[64]; strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); snprintf(val, sizeof(val), "%d", fd); add_environment(key, val); /* make sure we don't close-on-exec */ fcntl(fd, F_SETFD, 0);}void notify_service_state(const char *name, const char *state){ char pname[PROP_NAME_MAX]; int len = strlen(name); if ((len + 10) > PROP_NAME_MAX) { return; } snprintf(pname, sizeof(pname), "init.svc.%s", name); property_set(pname, state);}void service_start(struct service *svc, const char *dynamic_args){ struct stat s; pid_t pid; int needs_console; int n; char *scon = NULL; int rc; svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); svc->time_started = 0; if (svc->flags & SVC_RUNNING) { return; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } /** * service一般运行于另外一个进程中,这个进程也是init的子进程 * 所以,启动service前需要判断对应的可执行文件是否存在 */ if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { // pid为0,表示运行在子线程中 struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); if (properties_inited()) { // 得到属性存储空间的信息并加到环境变量中 get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } // 添加环境变量信息 for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); // 根据socketinfo创建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); } } } // ......省略设置uid,gid代码 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 { // ......省略动态参数的execve执行代码 } if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); svc->pid = 0; return; } svc->time_started = gettime(); svc->pid = pid; svc->flags != SVC_RUNNING; /** * 每一个service都有一个属性 * 例如:zygote的属性为init.svc.zygote,设置当前属性值为running * / if (properties_inited()) { notify_service_state(svc->name, "running"); }}
通过上述代码,我们可以知道,原来service的启动是通过fork和execve共同实现的。