Anddroid启动service:restart_processes函数分析

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服务等。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值