目录
- 目录
- 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 2
int 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 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);
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_tail
void 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 default
class_start main
class_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共同实现的。