init程序在最后的for循环中会重新启动一些service,通过调用restart_processes函数,循环遍历service list中的每一个service。如果service设置了SVC_RESTARTING标志位,调用restart_service_if_needed函数尝试启动或重启该service。
restart_service_if_needed函数实现如下:
static void restart_service_if_needed(struct service *svc)
{
/* service的time_restarted为service上次启动的时间,初始为0 */
time_t next_start_time = svc->time_started + 5;
/* 如果是一个新的service,或者是时间已到的restarting状态的service,调用service_start启动并清除标志位 */
if (next_start_time <= gettime()) {
svc->flags &= (~SVC_RESTARTING);
service_start(svc, NULL);
return;
}
/* 还没有到这个service的restart时间,找出service list中最早还未被restart的service时间,赋值给process_needs_restart */
if ((next_start_time < process_needs_restart) ||
(process_needs_restart == 0)) {
process_needs_restart = next_start_time;
}
}
如果是一个新的从未启动的service,或者是一个time_restarted时间已到的service,会调用service_start函数启动或重启该service。另外由于每次调用restart_processes都会遍历service list,该函数还能够找出链表中time_restart最小的service,并把该时间赋值给process_needs_restart,该变量在init程序poll文件描述符时能够被用到。
service_start函数:
void service_start(struct service *svc, const char *dynamic_args)
{
struct stat s;
pid_t pid;
int needs_console;
int n;
/* starting a service removes it from the disabled or reset
* state and immediately takes it out of the restarting
* state if it was in there
*/
/*首先清空相应的标志位*/
svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
svc->time_started = 0;
/* running processes require no additional work -- if
* they're in the process of exiting, we've ensured
* that they will immediately restart on exit, unless
* they are ONESHOT
*/
/*已经是运行状态的service直接返回*/
if (svc->flags & SVC_RUNNING) {
return;
}
needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
/*如果是需要控制台环境但是没有控制台,设置SVC_DISABLED标志位后返回*/
if (needs_console && (!have_console)) {
ERROR("service '%s' requires console\n", svc->name);
svc->flags |= SVC_DISABLED;
return;
}
/* 在parse_service构建service的过程中,将配置文件中的service文件路径名赋值给svc->args
* 这里判断该可执行文件是否存在 */
if (stat(svc->args[0], &s) != 0) {
ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
svc->flags |= SVC_DISABLED;
return;
}
/* oneshot表示该service退出后不能再次运行 */
if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
svc->args[0]);
svc->flags |= SVC_DISABLED;
return;
}
NOTICE("starting '%s'\n", svc->name);
/* fork一个新进程 */
pid = fork();
/*pid为0表示子进程运行空间*/
if (pid == 0) {
struct socketinfo *si;
struct svcenvinfo *ei;
char tmp[32];
int fd, sz;
/* 如果property域已经被初始化了,增加一个property属性,用文件描述符和大小作值 */
if (properties_inited()) {
get_property_workspace(&fd, &sz);
sprintf(tmp, "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
}
/* 在配置文件中,service通过setenv设置envvars */
for (ei = svc->envvars; ei; ei = ei->next)
add_environment(ei->name, ei->value);
/* 在init.rc配置文件中,通过socket关键字设置service的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));
/* 创建socket */
int s = create_socket(si->name, socket_type,
si->perm, si->uid, si->gid);
if (s >= 0) {
/* 调用add_environment,ANDROID_SOCKET_%s(name),s(文件描述符)
* 再调用fcntl,make sure we don't close-on-exec */
publish_socket(si->name, s);
}
}
if (svc->ioprio_class != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
}
}
if (needs_console) {
/* 使进程独立开来,摆脱源会话、源进程组、源控制终端,使其成为一个新的进程组长 */
setsid();
open_console();
} else {
zap_stdio();
}
#if 0
for (n = 0; svc->args[n]; n++) {
INFO("args[%d] = '%s'\n", n, svc->args[n]);
}
for (n = 0; ENV[n]; n++) {
INFO("env[%d] = '%s'\n", n, ENV[n]);
}
#endif
/* 因为第一个参数为0,所以将当前进程的pgid设置成自己的pid,表示新建一个进程组? */
setpgid(0, getpid());
/* as requested, set our gid, supplemental gids, and uid */
if (svc->gid) {
if (setgid(svc->gid) != 0) {
ERROR("setgid failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->nr_supp_gids) {
if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
ERROR("setgroups failed: %s\n", strerror(errno));
_exit(127);
}
}
if (svc->uid) {
if (setuid(svc->uid) != 0) {
ERROR("setuid failed: %s\n", strerror(errno));
_exit(127);
}
}
/* 接下来就是execve,分静态参数和动态参数两种情况 */
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 {
/* 用函数的额外参数 */
char *arg_ptrs[INIT_PARSER_MAXARGS+1];
int arg_idx = svc->nargs;
char *tmp = strdup(dynamic_args);
char *next = tmp;
char *bword;
/* Copy the static arguments */
memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
/* 将参数用空格分割 */
while((bword = strsep(&next, " "))) {
arg_ptrs[arg_idx++] = bword;
if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
arg_ptrs[arg_idx] = '\0';
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
}
_exit(127);
}
/* 子进程创建失败 */
if (pid < 0) {
ERROR("failed to start '%s'\n", svc->name);
svc->pid = 0;
return;
}
/* init程序作为父进程,设置service的启动时间、pid和RUNNING标志位 */
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;
if (properties_inited())
/* 调用property_set设置一个service为running状态的property */
notify_service_state(svc->name, "running");
}
该函数通过fork系统调用为service创建一个新进程,因为执行service_start函数的总是init程序,所以init程序是所有service程序的父进程。
在父进程init的执行代码中,会设置service的启动时间,进程id和标志位等信息:
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;
而在子进程service中,会初始化service运行所需的一些信息。首先判断property area是否已经被初始化了,property area会在property_init_action被执行后初始化。如果已经被初始化,
首先通过dup(fd)返回一个新的文件描述符,该fd和init父进程一样指向的是
/dev/__properties__,只是是属于子进程service。然后调用add_environment函数往service的ENV数组中增加一个关于property area的环境信息。
ENV是一个长度32的char *数组,每个元素存储service的一个环境信息。
在解析init.rc文件创建service时,还可以指定service的环境信息,并将其存放在service的成员变量envvars链表中,子进程service遍历该链表,把每一个环境信息通过add_environment函数添加到ENV数组中。
随后根据init.rc配置的socket信息,调用create_socket创建相应的socket并bind相应的地址。
setpgid函数改变进程的pgid,因为提供的第一个参数是0,setpgid会将进程的pgid设置成自身的pid,这样每一个service相互独立,都会被当成一个新的进程组在运行。
设置完这些属性之后需要执行service对应的源程序,通过execve进行,相应的程序路径会保存在service的args[0]中。分为静态参数和动态参数两种情况。
静态参数表示参数就是存放在service结构体中的,
动态参数表示参数是通过service_start的第二个参数传递进来的。
对于静态参数的情况,在init.rc文件中配置service时可以带上多个参数,将其存放在service的args变量中。虽然在struct service中args的定义是char *args[1],长度为1,但是它在service结构体中位于最末端,在parse_service分配内存时会为其额外分配一些内存:
svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
nargs表示参数的个数加1,这样service的args就有足够的空间用来存储程序路径和参数信息。
最后通过调用execve,子进程service转去执行相应的service程序,如zygote、servicemanager等,服务真正的被启动。可以看到,init作为第一个初始化进程,扮演者非常重要的角色,是安卓系统中所有服务的父进程,管理这些service的运行状态,并提供property服务等。