Android Init进程

  Android系统在加载linux内核后,便启动第一个进程————init进程。
  init进程的入口为main函数,文件路径位于/system/core/init/init.cpp中。该进程的执行可分为两个阶段。

/system/core/init/init.cpp

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    ...

  argv[0]是程序的全路径名,如果文件名(basebname表示去掉路径和后缀名)是ueventd,则进入ueventd_main函数。ueventd主要负责创建设备节点文件。

/system/core/init/init.cpp

int ueventd_main(int argc, char **argv)
{
    /*
     * init sets the umask to 077 for forked processes. We need to
     * create files with exact permissions, without modification by
     * the umask.
     */
     //使创建的设备文件具有完全权限
    umask(000);

    /* Prevent fire-and-forget children from becoming zombies.
     * If we should need to wait() for some children in the future
     * (as opposed to none right now), double-forking here instead
     * of ignoring SIGCHLD may be the better solution.
     */
     //对子进程退出的信号处理设置为忽略
    signal(SIGCHLD, SIG_IGN);
    //SELinux为我们提供了一个/dev/null,这里将标准输入,标准输出,标准错误输出均重定位到/dev/null
    open_devnull_stdio();
    //打开/dev/kmsg日志设备
    klog_init();
    //设置kernel log等级
    klog_set_level(KLOG_NOTICE_LEVEL);

    NOTICE("ueventd started!\n");
    //与SEAndroid有关
    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    //hardware保存了ro.hardware的属性值
    char hardware[PROP_VALUE_MAX];
    property_get("ro.hardware", hardware);
    //解析文件ueventd.rc
    ueventd_parse_config_file("/ueventd.rc");
    //解析文件ueventd.(hardware).rc
    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
    //设备初始化
    device_init();
    //监听设备上的事件
    pollfd ufd;
    ufd.events = POLLIN;
    ufd.fd = get_device_fd();

    while (true) {
        ufd.revents = 0;
        int nr = poll(&ufd, 1, -1);
        if (nr <= 0) {
            continue;
        }
        if (ufd.revents & POLLIN) {
            handle_device_fd();
        }
    }

    return 0;
}

  继续分析main函数。

/system/core/init/init.cpp

    //启动watchdog
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }

    // Clear the umask.
    umask(0);
    //添加环境变量
    add_environment("PATH", _PATH_DEFPATH);
    //没有额外参数或者第一个参数不是"--second-stage"启动时为第一阶段
    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

  如果处于第一阶段,init进程会挂载一些目录,将tmpfs(虚拟内存文件系统,由Real Memory和swap构成)挂载在/dev上,devpts(远程虚拟终端)挂载在/dev/pts上,proc(访问内核信息的虚拟文件系统)挂载在/proc上,sysfs(设备驱动模型文件系统)挂载到/sys上。

/system/core/init/init.cpp

if (is_first_stage) {
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        mount("proc", "/proc", "proc", 0, NULL);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }

/system/core/init/init.cpp

    open_devnull_stdio();
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);
    ...
    selinux_initialize(is_first_stage);

/system/core/init/init.cpp

static void selinux_initialize(bool in_kernel_domain) {
    Timer t;
    //设置回调函数
    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    //SElinux没有启动,直接返回
    if (selinux_is_disabled()) {
        return;
    }
    //第一阶段
    if (in_kernel_domain) {
        //加载SELinux策略
        INFO("Loading SELinux policy...\n");
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();
        }
        //读取/proc/cmdline文件,根据androidboot.selinux的值决定返回值
        bool is_enforcing = selinux_is_enforcing();
        security_setenforce(is_enforcing);
        //向/sys/fs/selinux/checkreqprot写入1
        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();
        }

        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
    } else {//第二阶段
        selinux_init_all_handles();
    }
}

  看看selinux_is_enforcing()函数。

/system/core/init/init.cpp

static bool selinux_is_enforcing(void)
{
    if (ALLOW_DISABLE_SELINUX) {
        return selinux_status_from_cmdline() == SELINUX_ENFORCING;
    }
    return true;
}

/system/core/init/init.cpp

static selinux_enforcing_status selinux_status_from_cmdline() {
    selinux_enforcing_status status = SELINUX_ENFORCING;

    std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
        char *value = strchr(name, '=');
        if (value == nullptr) { return; }
        *value++ = '\0';
        if (strcmp(name, "androidboot.selinux") == 0) {
            if (strcmp(value, "disabled") == 0) {
                status = SELINUX_DISABLED;
            } else if (strcmp(value, "permissive") == 0) {
                status = SELINUX_PERMISSIVE;
            }
        }
    };
    import_kernel_cmdline(false, fn);

    return status;
}

  import_kernel_cmdline的作用在于读取/proc/cmdline文件的内容,以’ ’ 为分隔符,分割成一系列的字符串,依次作为第二个参数表示的函数的第一个参数传入。
  selinux_status_from_cmdline函数中的fn函数将这些分割过后的字符串再次以’=’作为分隔符进行分割,依照androidboot.selinux的值返回不同的值,默认是返回SELINUX_ENFORCING状态值。

/system/core/init/init.cpp

void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
{
    char cmdline[2048];
    char *ptr;
    int fd;

    fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
    if (fd >= 0) {
        int n = read(fd, cmdline, sizeof(cmdline) - 1);
        if (n < 0) n = 0;

        /* get rid of trailing newline, it happens */
        if (n > 0 && cmdline[n-1] == '\n') n--;

        cmdline[n] = 0;
        close(fd);
    } else {
        cmdline[0] = 0;
    }

    ptr = cmdline;
    while (ptr && *ptr) {
        char *x = strchr(ptr, ' ');
        if (x != 0) *x++ = 0;
        import_kernel_nv(ptr, in_qemu);
        ptr = x;
    }
}

  返回main函数。

/system/core/init/init.cpp

    if (is_first_stage) {
        //恢复SELinux文件属性即恢复文件的安全上下文
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        //重新执行main函数,并传入"--second-stage"参数,进入第二阶段。execv会停止执行当前的进程,并且以新的应用进程替换被停止执行的进程,进程ID没有改变。
        if (execv(path, args) == -1) {
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }

  除了一部分是第一阶段和第二阶段都要执行的操作外,很多property的初始化是在第二阶段完成的。

/system/core/init/init.cpp

if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        //测试/dev/.booting是否正常打开关闭
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        //打开/dev/__properties__文件
        property_init();

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        //先初始化DT,DT优先级比cmdline高
        process_kernel_dt();
        process_kernel_cmdline();

        // Propogate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();
    }

  读取/proc/device-tree/firmware/android/compatible文件内容,如果是”android,firmware”则报错直接返回。dir为打开/proc/device-tree/firmware/android目录的DIR类型,unique_ptr第二个模板参数为删除器。接下来读取目录下的文件,跳过文件类型不是DT_REG或者文件名为”compatible”的文件。依次读这些文件内容,将里面的’,’换成’.’。最后以ro.boot.(文件名)为key,读取的文件内容为value设置property。

/system/core/init/init.cpp

static void process_kernel_dt(void)
{
    static const char android_dir[] = "/proc/device-tree/firmware/android";

    std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);

    std::string dt_file;
    android::base::ReadFileToString(file_name, &dt_file);
    if (!dt_file.compare("android,firmware")) {
        ERROR("firmware/android is not compatible with 'android,firmware'\n");
        return;
    }

    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
    if (!dir)
        return;

    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
            continue;

        file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
        property_set(property_name.c_str(), dt_file.c_str());
    }
}

  前面提到import_kernel_cmdline将/proc/cmdline文件内容按’ ‘分割开来,这里将分割后的字符串传给import_kernel_nv的name参数。这里讲name按’=’进行分割,name设为’=’号前面部分,value设为’=’后面部分。name为”qemu”时,将对应的value拷贝到qemu数组中。name的前12个字符为”androidboot.”时,boot_prop_name记为name中”androidboot.”后面部分,以ro.boot.(boot_prop_name)为key,value字符串为value设置property。

/system/core/init/init.cpp

static void process_kernel_cmdline(void)
{
    /* don't expose the raw commandline to nonpriv processes */
    chmod("/proc/cmdline", 0440);

    /* first pass does the common stuff, and finds if we are in qemu.
     * second pass is only necessary for qemu to export all kernel params
     * as props.
     */
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0])
        import_kernel_cmdline(true, import_kernel_nv);
}

/system/core/init/init.cpp

static void import_kernel_nv(char *name, bool for_emulator)
{
    char *value = strchr(name, '=');
    int name_len = strlen(name);

    if (value == 0) return;
    *value++ = 0;
    if (name_len == 0) return;

    if (for_emulator) {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char buff[PROP_NAME_MAX];
        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );

        if (len < (int)sizeof(buff))
            property_set( buff, value );
        return;
    }

    if (!strcmp(name,"qemu")) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;

        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}

  export_kernel_boot_props用于根据prop_map这个结构体数组设置property。以src_prop为key获取属性值,保存在value中。若获取成功,以dst_prop为key,value值为value设置property。若获取不成功,则以dst_prop为key,对应的default_value为value设置property。

/system/core/init/init.cpp

static void export_kernel_boot_props() {
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno",   "ro.serialno",   "", },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };
    for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
        char value[PROP_VALUE_MAX];
        int rc = property_get(prop_map[i].src_prop, value);
        property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
    }
}

  接着回到main函数分析init的第二阶段。接着创建一个epoll句柄。

/system/core/init/init.cpp

    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }

  socketpair函数建立一对匿名的已经连接的套接字,相当于创建一个双向管道。不过这里将s[0]作为写端,s[1]作为读端。接下来对SIGCHLD信号作处理,SIGCHLD_handler作为信号处理函数,功能是往s[0]端写入一个数字”1”。SA_NOCLDSTOP表示进程若处于停止状态,不对信号作处理。reap_any_outstanding_children对之前退出的子进程作处理。register_epoll_handler将是s[0]套接字注册到epoll中,监听其EPOLLIN事件。例如,当servicemanager退出时,会向父进程init发送一个SIGCHLD信号,信号处理函数SIGCHLD_handler会向管道里写入一个”1”,因为管道读端已被epoll监控,所以相应的epoll_wait会返回。register_epoll_handler在注册epoll_event时同时也在epoll_event的联合体成员epoll_data中保存了handle_signal的函数指针,这样在别的地方epoll_wait返回之后便可以获得这个函数指针。handle_signal中调用了reap_any_outstanding_children,会对退出的子进程对应的service作处理,在reap_any_outstanding_children里面的wait_for_one_process的末尾会进行restart zygote的操作。

/system/core/init/init.cpp

    signal_handler_init();

/system/core/init/signal_handler.cpp

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    reap_any_outstanding_children();
}

/system/core/init/init.cpp

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    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.
    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);
}

  看看reap_any_outstanding_children如何处理退出的子进程。

/system/core/init/signal_handler.cpp

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) {
    }
}

  wait_for_one_process中,首先使用waitpid等待子进程结束,参数中-1表示等待任意的子进程,WNOHANG为非阻塞标志。根据waitpid返回的子进程pid,找到对应的struct service变量svc。如果svc标志位有SVC_RESTART,或者没有SVC_ONESHOT,杀掉进程组号为pid的所有进程,因为这些进程都是需要自动重启的,之前的进程组也就没有必要留下来了。
  kill函数的pid参数决定其函数功能。1.pid大于零时,pid是信号欲送往的进程的标识。2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。4. pid小于-1时,信号将送往以-pid为组标识的进程。
  struct service的sockets成员是一个链表结构。tmp字符串为”/dev/socket/(svc->name)”。之后使用unlink删除这些tmp字符串表示的套接字文件。
  如果svc有SVC_EXEC标志位,说明这个service已经执行完毕,则释放掉svc,返回true。之后将svc的pid成员置0,取消svc的SVC_RUNNING标志位。如果svc设有SVC_ONESHOT标志位而没有设SVC_RESTART标志位,为svc增设SVC_DISABLED标志位。如果svc同时设立了SVC_DISABLED和SVC_RESET标志位,增设key为init.svc.(svc->name),value为”stopped”的property,返回true。如果svc设立了SVC_CRITICAL和SVC_RESTART标志,4分钟内发生crash次数大于4次,则重启设备进入recovery。
  之后取消svc的SVC_RESTART标志,增设SVC_RESTARTING的标志。svc->onrestart.commands是重启要执行的commands链表,具体表现为init.rc里面service部分关于onrestart的选项。接下来将这些commands依次执行。设置“restarting”的property之后,返回true。设置了SVC_RESTARTING的标志的service会在主循环中根据具体情况被重启。

/system/core/init/signal_handler.cpp

static bool wait_for_one_process() {
    int status;
    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;
    }

    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.

    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.
    for (socketinfo* si = svc->sockets; si; si = si->next) {
        char tmp[128];
        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
        unlink(tmp);
    }

    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.
    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();
    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.
    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;
}

  回到main函数。init_parse_config_file用来解析init.rc文件。解析规则大致说明一下:
  init.rc文件包含三部分内容:import,Action,Service。import类似于C语言中的include,将其他rc文件load进来读取。Action的语法格式如下,对应struct action。trigger是触发器,满足这个触发器就会执行下面的command。init.rc目前支持五类触发器。
1.boot

   这是init执行后第一个被触发Trigger,也就是在/init.rc被装载之后执行该Trigger。

2.name=value

  当属性name被设置成value时触发。例如:onproperty:vold.decrypt=trigger_reset_main。

3.device-added

  当设备节点被添加时触发。

4.device-removed

  当设备节点被移除时添加。

5.service-exited

  会在一个特定的服务退出时触发。

  Services的选项(option)是服务的修饰符,可以影响服务如何以及怎样运行。服务支持的选项如下:
1.critical

  表明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。

2.disabled

  表明这个服务不会自动启动。该服务必须被明确的按名启动。

3.setenv

  在进程启动时设置环境变量。

4.socket {name type perm user group}

  创建一个unix域的名为/dev/socket/(name) 的套接字,并传递它的文件描述符给已启动的进程。type 必须是 “dgram”,”stream” 或”seqpacket”。用户user和组group默认是0。

5.user {username}

  在启动这个服务前改变该服务的用户名。此时默认为 root。

6.group {groupname} {groupname}

  在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),默认是root。

7.oneshot

  服务退出时不重启。

8.class name

  指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为”default”类服务。在Action语法中,我们可以使用class_start, class_reset, class_stop等操作来操作这些同属一类的service。

9.onrestart

  当服务重启,执行一个命令。

/system/core/init/init.cpp

init_parse_config_file("/init.rc");

/system/core/rootdir/init.rc

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

Action语法格式

on <trigger>  
   <command>  
   <command>  
   <command>  

Service语法格式

service <name> <pathname> [ <argument> ]*  
      <option>  
      <option>  

  回到main函数。action_list是所有解析出来的 action的alist成员组成的链表,这些alist在读取init.rc的时候被加入到链表中,链表记录着所有的action。node_to_item根据链表每个alist的地址确定action的地址act。
遍历action_list中的所有对应的action,再遍历每个action中的trigger链表,找到trigger name为”early-init”对应的action。使用action_add_queue_tail将这个action的qlist成员添加到action_queue中。

/system/core/init/init.cpp

action_for_each_trigger("early-init", action_add_queue_tail);

/system/core/init/init_parser.cpp

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node, *node2;
    struct action *act;
    struct trigger *cur_trigger;

    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        list_for_each(node2, &act->triggers) {
            cur_trigger = node_to_item(node2, struct trigger, nlist);
            if (!strcmp(cur_trigger->name, trigger)) {
                func(act);
            }
        }
    }
}

  返回到main函数中。接下来调用四个queue_builtin_action更新action_list和action_queue链表。queue_builtin_action分配了一个action,trigger,command。trigger的name为第二个参数name,添加到action的triggers链表中。command的func为第一个参数func,args[0]为第二个参数name,nargs为1,表示这个command是一个以nargs和args[0]为参数的函数。将该command添加到action的commands链表中。最后,将这些初始化了trigger和command的action的alist成员添加到action_list中,qlist成员添加到action_queue中。

/system/core/init/init_parser.cpp

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

/system/core/init/init_parser.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);
}

  可见,在”early-init”这一阶段,将trigger为”early-init”的action和新建的command分别为wait_for_coldboot_done_action,mix_hwrng_into_linux_rng_action,keychord_init_action,console_init_action的action加入到action_queue中去。
  init阶段可以按同样的方法分析。

/system/core/init/init.cpp

   action_for_each_trigger("init", action_add_queue_tail);

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

  ro.bootmode的属性值为”charger”时,表示要进入充电模式,此时将trigger的name为charger的action添加到action_queue中去。否则,进入到late-init阶段。值得指出的是,这时候action_list里面是解析init.rc得到的和内建的action,action_queue里面是使用action_for_each_trigger和queue_builtin_action添加进去的action,在execute_one_command中被依次取出执行。
/system/core/init/init.cpp

    char bootmode[PROP_VALUE_MAX];
    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
    }

    // Run all property triggers based on current state of the properties.
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

  接下来进入循环部分。

/system/core/init/init.cpp

   while (true) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }

        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action) {
            timeout = 0;
        }

        bootchart_sample(&timeout);

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

  waiting_for_exec是一个bool变量,它的值被初始化为false,只会在下面的restart_processes函数中改变。首先调用的是execute_one_command函数。action_remove_queue_head从action_queue取下头结点对应的action赋给cur_action。get_first_command取出cur_action的command节点的下一个作为cur_command。is_last_command判断cur_command的下一个command节点是否是cur_action的command节点,如果是返回true,则说明对cur_action的command链表已经遍历完毕。当is_last_command返回false,且cur_action和cur_command均存在对应节点时,使用get_next_command获取cur_command链表的下一个作为cur_command,cur_command链表遍历完时返回null,cur_command为null就重新从action_queue中摘下一个新的action头节点开始新的遍历。拿到一个cur_command后,以command的成员nargs和args为参数调用command的成员func函数。

/system/core/init/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)) {
        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);
        cur_command = get_first_command(cur_action);
    } else {
        cur_command = get_next_command(cur_action, cur_command);
    }

    if (!cur_command) {
        return;
    }

    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());
    }
}

  service_list在读取init.rc的时候被添加了各种service。service_for_each_flags就是遍历service_list链表,对其中有SVC_RESTARTING标志的service调用restart_service_if_needed函数。process_needs_restart是一个time_t变量,标记了service重启的预定时间,service的time_t成员time_started标记了上次service启动的事件。restart_service_if_needed将next_start_time设为time_started + 5,若next_start_time比当前时间小,则立刻启动service。否则,若next_start_time比process_needs_restart小或者process_needs_restart为0时,将process_needs_restart设置为next_start_time。

/system/core/init/init.cpp

static void restart_processes()
{
    process_needs_restart = 0;
    service_for_each_flags(SVC_RESTARTING,
                           restart_service_if_needed);
}

/system/core/init/init_parser.cpp

void service_for_each_flags(unsigned matchflags,
                            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 (svc->flags & matchflags) {
            func(svc);
        }
    }
}

/system/core/init/init.cpp

static void restart_service_if_needed(struct service *svc)
{
    time_t next_start_time = svc->time_started + 5;

    if (next_start_time <= gettime()) {
        svc->flags &= (~SVC_RESTARTING);
        service_start(svc, NULL);
        return;
    }

    if ((next_start_time < process_needs_restart) ||
        (process_needs_restart == 0)) {
        process_needs_restart = next_start_time;
    }
}

  service_start中,首先取消掉SVC_DISABLED,SVC_RESTARTING,SVC_RESET,SVC_RESTART,SVC_DISABLED_START的标志位,time_started记为0。当svc设置了标志位SVC_RUNNING,直接返回,因为已经在运行中。当svc设置了标志位SVC_CONSOLE又没有对应的console时,设置标志位SVC_DISABLED,本次不需要启动该service,返回。
读取service的args成员信息失败时,设置标志位SVC_DISABLED,返回。如果没有设置SVC_ONESHOT而又传进了dynamic_args时,设置标志位SVC_DISABLED,返回。这个dynamic_args是用来手动启动设置了SVC_ONESHOT的service的。
  接下来是对SELinux处理的内容,此处先跳过。接下来fork出一个子进程,在子进程设置了一些service的属性,并运行这个service。父进程将time_started设为当前时间,并为service添加SVC_RUNNING标志位,将waiting_for_exec设成true,最后将这个service对应的属性值设为”running”。
/system/core/init/init.cpp

void service_start(struct service *svc, const char *dynamic_args)
{
    // 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_RESTART|SVC_DISABLED_START));
    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.
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    bool needs_console = (svc->flags & SVC_CONSOLE);
    if (needs_console && !have_console) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat s;
    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    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;
    }

    char* scon = NULL;
    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
            if (!scon) {
                ERROR("Out of memory while starting '%s'\n", svc->name);
                return;
            }
        } else {
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            int rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }

            rc = getfilecon(svc->args[0], &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                freecon(mycon);
                return;
            }

            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
            if (rc == 0 && !strcmp(scon, mycon)) {
                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
            }
            freecon(mycon);
            freecon(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }
        }
    }

    NOTICE("Starting service '%s'...\n", svc->name);

    pid_t pid = fork();
    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_initialized()) {
            get_property_workspace(&fd, &sz);
            snprintf(tmp, sizeof(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);

        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, si->socketcon ?: scon);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        freecon(scon);
        scon = NULL;

        if (svc->writepid_files_) {
            std::string pid_str = android::base::StringPrintf("%d", pid);
            for (auto& file : *svc->writepid_files_) {
                if (!android::base::WriteStringToFile(pid_str, file)) {
                    ERROR("couldn't write %s to %s: %s\n",
                          pid_str.c_str(), file.c_str(), strerror(errno));
                }
            }
        }

        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 (false) {
            for (size_t n = 0; svc->args[n]; n++) {
                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
            }
            for (size_t n = 0; ENV[n]; n++) {
                INFO("env[%zu] = '%s'\n", n, ENV[n]);
            }
        }

        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);
            }
        }
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }

        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] = NULL;
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    freecon(scon);

    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;

    if ((svc->flags & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
             svc->seclabel ? : "default");
        waiting_for_exec = true;
    }

    svc->NotifyStateChange("running");
}

  回到main函数,末尾使用epoll_wait监听子进程退出事件,若成功返回,调用handle_signal函数。handle_signal会读取管道的内容,把管道读端的内容清空,然后调用reap_any_outstanding_children处理退出的进程,在前面已有介绍。

/system/core/init/init.cpp

...
        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

/system/core/init/init_parser.cpp

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    reap_any_outstanding_children();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Invoker123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值