init为安卓系统开启后,由kernel引导的第一个进程,其通过解析代码中不同的init.rc脚本,不断的将系统服务启动起来,具体的脚本文件已在之前博文中进行了介绍。本章节主要介绍init进程的启动流程。
init流程分析
主要任务
- 第一个应用程序
- 创建目录,挂在分区
- 解析应用脚本
- 启动服务
- 守护服务
主要过程
- 创建目录
- klog_init() 将log重定向到/proc/kmsg中
- property_init() 初始化属性服务
- signal_handler_init() 初始化子进程退出的信号处理过程
- property_load_boot_defaults() 初始化环境变量
- init_parse_config_file(“/init.rc”) 解析init.rc
- action_for_each_trigger(…) 执行rc脚本文件中对应的触发器
- queue_builtin_action(…) 构建自定义事件,加入队列
- execute_one_command() 死循环,执行脚本命令(启动进程)
- restart_processes() 重启服务进程(守护进程)
- epoll_wait() 监听管道,等待事件
- 在分析init.cpp的具体流程,先来回顾下init.rc脚本的主要组成部分
rc脚本文件可以用以下几张图片概括
- 文件结构

init.rc文件路径在/system/core/rootdir/init.rc下存在,根据不同的平台又有自己的rc脚本,通过import导入
- 导包内容

其中 ${ ro.hardware } 的值可以通过 getprop ro.harware 命令获取,其对应于特定平台的rc文件,例如 getprop ro.hardware = mooney , 那么导包对应的rc脚本文件为init.mooney.rc
- 触发器

脚本中声明多个 on ... 触发器,可以通过代码action_for_each_trigger(...)触发实现
- service服务

service服务脚本声明:指定脚本文件名称,路径,启动参数,类属以及其他属性值
init进程具体流程分析
init 主要的执行流程如下图所示

初始化 kernel log ,位于设备节点/dev/kmsg
–> klog.c
void klog_init(void) {
if (klog_fd >= 0) return; /* Already initialized */
// 打开指定设备
klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
if (klog_fd >= 0) {
return;
}
static const char* name = "/dev/__kmsg__";
// 创建节点
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
// 尝试打开
klog_fd = open(name, O_WRONLY | O_CLOEXEC);
unlink(name);
}
}
初始化子进程退出的信号处理过程signal_handler_init()
–> signal_handler.cpp
void signal_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
// 创建socket pair套接字
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
ERROR("socketpair failed: %s\n", strerror(errno));
exit(1);
}
// 赋值
signal_write_fd = s[0];
signal_read_fd = s[1];
// Write to signal_write_fd if we catch SIGCHLD.
// 当捕获SIGCHLD信号,则写入signal_write_fd
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIGCHLD_handler;
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
reap_any_outstanding_children();
// 注册监听处理
register_epoll_handler(signal_read_fd, handle_signal);
}
static void reap_any_outstanding_children() {
while (wait_for_one_process()) {
}
}
static bool wait_for_one_process() {
int status;
//等待任意子进程,如果子进程没有退出则返回0,否则返回盖子进程的pid
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
ERROR("waitpid failed: %s\n", strerror(errno));
return false;
}
//根据pid查找相应的service
service* svc = service_find_by_pid(pid);
std::string name;
if (svc) {
name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
if (!svc) {
return true;
}
// TODO: all the code from here down should be a member function on service.
// 当flag为RESTART,且不是ONESHOT时,先kill进程组内的所有的子进程或子线程
if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
kill(-pid, SIGKILL);
}
// Remove any sockets we may have created.
// 移除当前服务中所有创建过的socket
for (socketinfo* si = svc->sockets; si; si = si->next) {
char tmp[128];
snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
unlink(tmp);
}
// 当flag为EXEC时,释放相应的服务
if (svc->flags & SVC_EXEC) {
INFO("SVC_EXEC pid %d finished...\n", svc->pid);
waiting_for_exec = false;
list_remove(&svc->slist);
free(svc->name);
free(svc);
return true;
}
svc->pid = 0;
svc->flags &= (~SVC_RUNNING);
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
// 对于ONESHOT服务,使其进入disabled状态
if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
// Disabled and reset processes do not get restarted automatically.
// 禁用和重置的服务,都不在自动重启
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped");
return true;
}
time_t now = gettime();
// 服务在4分钟内重启次数超过4次,则重启进入recovery模式
if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
ERROR("critical process '%s' exited %d times in %d minutes; "
"rebooting into recovery mode\n", svc->name,
CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
return true;
}
} else {
svc->time_crashed = now;
svc->nr_crashed = 1;
}
}
svc->flags &= (~SVC_RESTART);
svc->flags |= SVC_RESTARTING;
// Execute all onrestart commands for this service.
// 执行当前service中的所有onstart命令
struct listnode* node;
list_for_each(node, &svc->onrestart.commands) {
command* cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
svc->NotifyStateChange("restarting");
return true;
}
加载 *.prop 属性文件
–> property_service.cpp
void property_load_boot_defaults() {
//加载指定文件路径下的 *.prop 文件
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
if (read_file(filename, &data)) {
data.push_back('\n');
load_properties(&data[0], filter);
}
NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}
//解析文件到指定数据data结构中
static void load_properties(char *data, const char *filter)
{
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
if (filter) {
flen = strlen(filter);
}
sol = data;
while ((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
while (isspace(*key)) key++;
if (*key == '#') continue;
tmp = eol - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
// import节点
if (!strncmp(key, "import ", 7) && flen == 0) {
fn = key + 7;
while (isspace(*fn)) fn++;
key = strchr(fn, ' ');
if (key) {
*key++ = 0;
while (isspace(*key)) key++;
}
// 导入import节点文件,再次解析
load_properties_from_file(fn, key);
} else {
value = strchr(key, '=');
if (!value) continue;
*value++ = 0;
tmp = value - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
while (isspace(*value)) value++;
if (flen > 0) {
if (filter[flen - 1] == '*') {
if (strncmp(key, filter, flen - 1)) continue;
} else {
if (strcmp(key, filter)) continue;
}
}
// 设置属性
property_set(key, value);
}
}
}
启动属性服务器(通过sockert通信)
–> property_service.cpp
// 开启属性服务
void start_property_service() {
// 创建seocker套接字
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
// 加入监听
listen(property_set_fd, 8);
// 注册监听
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
用户可以通过串口开启、结束指定服务,通过命令setprop更改系统属性值,三方app也可以通过指定的api更改系统属性(权限限制),其到底层都是通过socket机制,通过init中的handle_property_***()方法实现。其流程模型如下:

解析 *.rc 脚本文件

–> init_parser.cpp
// 解析指定路径下文件
int init_parse_config_file(const char* path) {
INFO("Parsing %s...\n", path);
Timer t;
std::string data;
// 读取文件信息到data中
if (!read_file(path, &data)) {
return -1;
}
data.push_back('\n'); // TODO: fix parse_config.
parse_config(path, data);
dump_parser_state();
// MStar Android Patch Begin
INFO("(Parsing %s took %.2fs.)\n", path, t.duration());
// MStar Android Patch End
return 0;
}
// 解析脚本文件信息
static void parse_config(const char *fn, const std::string& data)
{
struct listnode import_list;
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
int nargs = 0;
parse_state state;
state.filename = fn;
state.line = 0;
state.ptr = strdup(data.c_str()); // TODO: fix this code!
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 (kw_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:
list_for_each(node, &import_list) {
struct import *import = node_to_item(node, struct import, list);
int ret;
// 解析导入文件
ret = init_parse_config_file(import->filename);
if (ret)
ERROR("could not import file '%s' from '%s'\n",
import->filename, fn);
}
}
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;
}
}
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone;
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"':
x++;
for (;;) {
switch (*x) {
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++;
}
}
break;
case '\\':
x++;
switch (*x) {
case 0:
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
}
}
return T_EOF;
}
// 根据命令匹配相应的操作
// 通过宏定义匹配相应的操作指令
static int lookup_keyword(const char *s)
{
switch (*s++) {
case 'b':
if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
break;
case 'c':
if (!strcmp(s, "opy")) return K_copy;
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, "nable")) return K_enable;
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;
if (!strcmp(s, "nstallkey")) return K_installkey;
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;
if (!strcmp(s, "oad_all_props")) return K_load_all_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, "estorecon_recursive")) return K_restorecon_recursive;
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, "etenv")) return K_setenv;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
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 'v':
if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
if (!strcmp(s, "ritepid")) return K_writepid;
if (!strcmp(s, "ait")) return K_wait;
break;
}
return K_UNKNOWN;
}
根据脚本文件不同的标签分类解析

static 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) {
// service 标签
case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
// on 标签
case K_on:
state->context = parse_action(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_action;
return;
}
break;
// import 标签
case K_import:
parse_import(state, nargs, args);
break;
}
state->parse_line = parse_line_no_op;
}
通过不同标签分类解析并加入对应的链表

// 解析脚本中的service节点加入链表
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
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;
}
service* svc = (service*) 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 = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
if (!svc) {
parse_error(state, "out of memory\n");
return 0;
}
svc->name = strdup(args[1]);
svc->classname = "default";
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
svc->args[nargs] = 0;
svc->nargs = nargs;
list_init(&svc->onrestart.triggers);
cur_trigger->name = "onrestart";
list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
list_init(&svc->onrestart.commands);
list_add_tail(&service_list, &svc->slist);
return svc;
}
// 解析service的具体信息并加入链表
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc = (service*) 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_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 {
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|idle> <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 = (int*) 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:
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 = (command*) 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: { /* name value */
if (nargs < 3) {
parse_error(state, "setenv option requires name and value arguments\n");
break;
}
svcenvinfo* ei = (svcenvinfo*) 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: {/* name type perm [ uid gid context ] */
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
break;
}
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;
}
socketinfo* si = (socketinfo*) 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]);
if (nargs > 6)
si->socketcon = args[6];
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_seclabel:
if (nargs != 2) {
parse_error(state, "seclabel option requires a label string\n");
} else {
svc->seclabel = args[1];
}
break;
case K_writepid:
if (nargs < 2) {
parse_error(state, "writepid option requires at least one filename\n");
break;
}
svc->writepid_files_ = new std::vector<std::string>;
for (int i = 1; i < nargs; ++i) {
svc->writepid_files_->push_back(args[i]);
}
break;
default:
parse_error(state, "invalid option '%s'\n", args[0]);
}
}
// 解析脚本中的action节点并加入链表中
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
struct trigger *cur_trigger;
int i;
if (nargs < 2) {
parse_error(state, "actions must have a trigger\n");
return 0;
}
action* act = (action*) calloc(1, sizeof(*act));
list_init(&act->triggers);
for (i = 1; i < nargs; i++) {
if (!(i % 2)) {
if (strcmp(args[i], "&&")) {
struct listnode *node;
struct listnode *node2;
parse_error(state, "& is the only symbol allowed to concatenate actions\n");
list_for_each_safe(node, node2, &act->triggers) {
struct trigger *trigger = node_to_item(node, struct trigger, nlist);
free(trigger);
}
free(act);
return 0;
} else
continue;
}
cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
cur_trigger->name = args[i];
list_add_tail(&act->triggers, &cur_trigger->nlist);
}
list_init(&act->commands);
list_init(&act->qlist);
list_add_tail(&action_list, &act->alist);
/* XXX add to hash */
return act;
}
// 解析脚本中action的具体信息,加入action链表中
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
struct action *act = (action*) state->context;
int kw, n;
if (nargs == 0) {
return;
}
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) {
parse_error(state, "invalid command '%s'\n", args[0]);
return;
}
n = kw_nargs(kw);
if (nargs < n) {
parse_error(state, "%s requires %d %s\n", args[0], n - 1,
n > 2 ? "arguments" : "argument");
return;
}
command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->line = state->line;
cmd->filename = state->filename;
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
list_add_tail(&act->commands, &cmd->clist);
}
static void parse_import(struct parse_state *state, int nargs, char **args)
{
struct listnode *import_list = (listnode*) state->priv;
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 handling import on line '%d' in '%s'\n",
state->line, state->filename);
return;
}
struct import* import = (struct import*) calloc(1, sizeof(struct import));
import->filename = strdup(conf_file);
list_add_tail(import_list, &import->list);
INFO("Added '%s' to import list\n", import->filename);
}
{% asset_img jiaobenfenxijieguo.png jiaobenfenxijieguo.png %}
执行脚本中指定触发器的操作
–> init.cpp
// 找到解析列表的action并运行
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node, *node2;
struct action *act;
struct trigger *cur_trigger;
// 找到脚本文件中指定的node节点
list_for_each(node, &action_list) {
// 获取指定节点的action
act = node_to_item(node, struct action, alist);
// 执行节点下fun()
list_for_each(node2, &act->triggers) {
cur_trigger = node_to_item(node2, struct trigger, nlist);
if (!strcmp(cur_trigger->name, trigger)) {
func(act);
}
}
}
}
通过指定的触发器,遍历解析的rc脚本文件相应command,执行具体的操作,流程图如下:

构建事件,并加入队列中
–> init.cpp
// 构建事件,并加入队列中
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
{
action* act = (action*) calloc(1, sizeof(*act));
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
cur_trigger->name = name;
list_init(&act->triggers);
list_add_tail(&act->triggers, &cur_trigger->nlist);
list_init(&act->commands);
list_init(&act->qlist);
command* cmd = (command*) calloc(1, sizeof(*cmd));
cmd->func = func;
cmd->args[0] = const_cast<char*>(name);
cmd->nargs = 1;
list_add_tail(&act->commands, &cmd->clist);
list_add_tail(&action_list, &act->alist);
action_add_queue_tail(act);
}
根据构建自定事件,并加入对列中,通指定触发器来触发操作,增加灵活性,如下图所示流程:

开启服务(死循环)
–> init.cpp
// 执行命令
void execute_one_command() {
Timer t;
char cmd_str[256] = "";
char name_str[256] = "";
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
// 获取链表的第一个action
cur_action = action_remove_queue_head();
cur_command = NULL;
if (!cur_action) {
return;
}
build_triggers_string(name_str, sizeof(name_str), cur_action);
INFO("processing action %p (%s)\n", cur_action, name_str);
// 获取第一条command命令
cur_command = get_first_command(cur_action);
} else {
// 下一跳command命令
cur_command = get_next_command(cur_action, cur_command);
}
if (!cur_command) {
return;
}
// 调用相应command的fun() 方法
int result = cur_command->func(cur_command->nargs, cur_command->args);
if (klog_get_level() >= KLOG_INFO_LEVEL) {
for (int i = 0; i < cur_command->nargs; i++) {
strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
if (i < cur_command->nargs - 1) {
strlcat(cmd_str, " ", sizeof(cmd_str));
}
}
char source[256];
if (cur_command->filename) {
snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
} else {
*source = '\0';
}
INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
cmd_str, cur_action ? name_str : "", source, result, t.duration());
}
}
// 返回头部action并移除队列
struct action *action_remove_queue_head(void)
{
if (list_empty(&action_queue)) {
return 0;
} else {
struct listnode *node = list_head(&action_queue);
struct action *act = node_to_item(node, struct action, qlist);
list_remove(node);
list_init(node);
return act;
}
}
如下所示,是init进程执行开启服务的具体流程:
- 首先从action_queue中查找到cmd
- 根据cmd对应的宏定义KEYWORD
- 执行对应的func方法
- 例如service,遍历解析的services列表,找到指定的service
- 启动指定的service服务

守护进程,重启服务
–> init.cpp
// 重启服务
static void restart_processes()
{
process_needs_restart = 0;
// 根据flag=SVC_RESTARTING,有选择的重启
service_for_each_flags(SVC_RESTARTING,
restart_service_if_needed);
}
// 根据服务名称找到解析列表中对应的服务并启动起来
void service_for_each_class(const char *classname,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
// 找到列表中对应服务的节点
list_for_each(node, &service_list) {
svc = node_to_item(node, struct service, slist);
if (!strcmp(svc->classname, classname)) {
// 启动服务
func(svc);
}
}
}

- 内核通过fork产生父子两个进程,之间通过socket产生通信
- init进程死循环等待socket内的消息
- 如果出现服务死掉,根据进程pid,找到脚本解析文件中对应的service
- 启动找到的service服务(是否起启动取决service中具体的约束内容)
具体的消息传递流程如下:

总结
- init 脚本中将服务统一归纳起来,由 init.cpp 统一解析,开启服务并守护,实现了两者之间的解耦
- 通过import导入相关脚本文件,初始化属性服务,开启socket通信处理,解析脚本文件,开启守护进程服务,死循环等待socket通信
- 一旦有服务异常死掉,便会通过socket通信机制,依据pid找到之前解析的脚本内容,重启服务
- 可以适当在脚本中添加代码实现自定义开机启动服务