Android系统启动流程(1) —— 解析init进程启动过程

前言

        本系列文章简要介绍 Android 系统启动的流程,不会拘泥于源码细节,旨在让读者了解大概的流程。另外,需要提醒大家注意的一点是,阅读本章需要有一定的 C/C++基础,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程,本系列文章都将基于Android8.1源码来分析Android系统的启动流程。

init 进程启动过程

       init进程是Android系统中用户空间的第一个进程,进程号为1,是Android系统启动流程中的一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建Zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些源文件位于源码目录system/core/init中。

    1 引入init 进程

      为了讲解 init 进程,首先要了解 Android 系统启动流程的前几步,以引入 init 进程。

      1. 启动电源以及系统启动
       当电源键按下时,引导 芯片代码从预定义的地方(固化在 ROM  )开始执行。加载引导程序 BootLoader 到队 中,然后执行。
       2. 引导程序 Bootloader
       引导程序 BootLoader 是在 Android 操作系统开始运行前的一 个小 程序,它的主要作用是把操作系统 拉起来并运行。
       3. Linux 内核启动
       当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设 置后,它首 先在系统文件中寻找 init.rc 文件,并启动 init 进程。
       4 . init 进程启动
       init 进程做的工作比较多 ,主 要用来初始化和启动属性服务和启动 z ygote 进程。
 
        从上面的步骤可以看 出, 当我们按下电源键时, 引导 芯片会加载运行引导程序, 引导 程序又启动 Linux 内核,在 Linux内核 加载 完成后,第一件事就是要启动 init 进程。

     2 init 进程的入口函数

       在Linux内核加载完成之后,它首先在系统文件中寻找init.rc文件,并启动init进程,然后查看init进程的入口函数main,代码如下所示:

system/core/init/init.cpp

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

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

    // ... 3
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    
    // 添加环境变量
    add_environment("PATH", _PATH_DEFPATH);

    // 获取本次启动是否是系统启动的第一阶段,如果是第一阶段,
    // 则进入下面的if语句中,创建并挂载相关的文件系统。
    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

    //创建文件并挂载
    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask.
        umask(0);

        // Get the basic filesystem setup we need put together in the initramdisk
        // on / and then we'll let the rc file figure out the rest.
        // 创建和挂载启动所需的文件目录
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
        // talk to the outside world...
        // 初始化Kernel的Log,这样就可以从外界获取Kernel的日志
        InitKernelLogging(argv);

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) {
            LOG(ERROR) << "Failed to mount required partitions early ...";
            panic();
        }

        SetInitAvbVersionInRecovery();  // ... 4

        // Set up SELinux, loading the SELinux policy.
        // 启动SELinux。
        selinux_initialize(true);

        // We're in the kernel domain, so re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (selinux_android_restorecon("/init", 0) == -1) {
            PLOG(ERROR) << "restorecon failed";
            security_failure();
        }

        setenv("INIT_SECOND_STAGE", "true", 1);

        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

        char* path = argv[0];
        char* args[] = { path, nullptr };
        execv(path, args);

        // execv() only returns if an error happened, in which case we
        // panic and never fall through this conditional.
        PLOG(ERROR) << "execv(\"" << path << "\") failed";
        security_failure();
    }

    // At this point we're in the second stage of init.
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    // Set up a session keyring that all processes will have access to. It
    // will hold things like FBE encryption keys. No process should override
    // its session keyring.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    // 初始化属性服务
    property_init();   //  .......  5

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();

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

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);

    // Clean up our environment.
    unsetenv("INIT_SECOND_STAGE");
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");

    // Now set up SELinux for second stage.
    selinux_initialize(false);
    selinux_restore_context();


    // 创建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(ERROR) << "epoll_create1 failed";
        exit(1);
    }

    // 用于设置子进程信号处理函数,如果子进程(Zygote进程)异常退出,init进程会调用该函数中
    // 设定的信号处理函数来进行处理
    signal_handler_init();   // ...... 6

    // 导入默认的环境变量
    property_load_boot_defaults();
    export_oem_lock_status();

    // 启动属性服务
    start_property_service();  // ...... 7

    set_usb_controller();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    ActionManager& am = ActionManager::GetInstance();
    ServiceManager& sm = ServiceManager::GetInstance();
    Parser& parser = Parser::GetInstance();

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        // 解析init.rc配置文件
        parser.ParseConfig("/init.rc"); // ...... 8
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }

    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();

    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

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

    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

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

    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
            // 遍历执行每个action中携带的command对应的执行函数
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
            if (!shutting_down) 
                // 重启死去的进程 
                restart_processes(); // ...... 9

            // If there's a process that needs restarting, wake up in time for that.
            if (process_needs_restart_at != 0) {
                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

       init的main函数中我们只需关注一下几点就可以了。

        注释1处判断当前进程是不是ueventd。init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。ueventd主要是负责设备节点的创建、权限设定等一系列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。

       ueventd通过两种方式创建设备节点文件。

      1.“冷插拔”(Cold Plug),即以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。

       2.“热插拔”(Hot Plug),即在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。

       注释2处判断当前进程是不是watchdogd。Android系统在长时间的运行下会面临各种软硬件的问题,为了解决这个问题,Android开发了WatchDog类作为软件看门狗来监控SystemServer中的线程,一旦发现问题,WatchDog会杀死system_server进程,system_server的父进程zygote接收到system_server的死亡信号后,会杀死自己。zygote进程死亡的信号传递到init进程后,init进程会杀死zygote进程所有的子进程并重启zygote

       注释3处判断是否紧急重启,如果是紧急重启,就安装对应的消息处理器。

      在系统启动的第一阶段中会清理umask,然后创建和挂载启动所需要的文件目录,其中挂载了 tmpfs、devpts、proc、sysfs和selinuxfs 共5种文件系统,这些都是系统运行时目录,这些目录只在系统运行时才会存在,系统停止时会消失。

       tmpfs:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。tmpfs既可以使用RAM,也可以使用交换分区,会根据你的实际需要而改变大小。tmpfs的速度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越。由于tmpfs是驻留在RAM的,因此它的内容是不持久的。断电后,tmpfs的内容就消失了,这也是被称作tmpfs的根本原因。

       devpts:为伪终端提供了一个标准接口,它的标准挂接点是/dev/ pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。

       proc:一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。

       sysfs:与proc文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。

       selinuxfs:用于支持SELinux的文件系统,SELinux提供了一套规则来编写安全策略文件,这套规则被称之为 SELinux Policy 语言

      在注释4处初始化安全框架AVB(Android Verified Boot),AVB主要用于防止系统文件本身被篡改,还包含了防止系统回滚的功能,以免有人试图回滚系统并利用以前的漏洞。

       在注释5处调用 property_init 函数来对属性进行初始化,并在注释7处调用 start_property_service 函数启动属性服务。

       在注释6处调用 signal_handler_init 函数用于设置子进程信号处理函数,它被定义在 system/core/init/ signal_handler.cpp 中,主要用于防止 init 进程fork出的子进程成为僵尸进程僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程为了防止僵尸进程的出现,系统会在子进程暂停和终止的时候发出 SIGCHLD 号,而 signal_handler_init 函数就是用来接收 SIGCHLD 信号的(其内部只处理进程终止的 SIGCHLD 信号)。

       假设 init 进程fork出的子进程 zygote 终止了, signal_handler_init 函数内部会调用 handle_signal 函数,最终会找到 zygote 进程并移除所有的 zygote 进程的信息,再重启 zygote 服务的启动脚本(比如 init.zygote64.rc )中带有 onrestart 选项的服务, 关于 init.zygote64 .re 后面会讲到,至于 zygote 进程本身会在注释9处被重启。这里只是拿 zygote 进程举个例子,其他 init 进程子进程的原理也是类似的。

       注释8处用来解析 init.rc 文件,解析 init.rc 的文件为 system/core/init/init_parse.cpp 文件, 接下来我们查看 init.rc 里做了什么。

     3 解析 init.rc

        init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,它主要包含五种类型语句:Action(Action中包含了一系列的Command)、Commands(init语言中的命令)、Services(由init进程启动的服务)、Options(对服务进行配置的选项)和Import(引入其他配置文件)。init.rc的配置代码如下所示。

system/core/rootdir/init. rc

on init
    sysclktz 0

    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /default.prop /dev/urandom
    
    ...

on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain

    ...

       这里只截取了一部分代码,其中#是注释符号。on init和on boot是Action类型语句,它的格式为:

on <trgger> [&& <trigger>]*
   <command>
   <command>
   <command>
   ...

      Action都是以on开始,随后会定义触发器(trigger),接着便是为其定义命令(Commmand),当触发器条件满足时,command会被执行。Action 型语句采用ActionParser 来进行解析。

      为了分析如何创建zygote进程,首先先了解Services类型语句,Service是一些在系统初始化时就启动或者退出时需要重启的程序。其格式如下:

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

       首先需要为服务定义名字,并指定程序路径,然后便是通过option来修饰服务(决定了服务在什么时候运行以及怎样运行), Service 型语句采用 ServiceParser 来进行解析

       需要注意 的是 An droid 8 .1  中对 init.rc 文件进行了拆分,每个服务对应一个 rc  文件。 我们要分析的 Zygote 启动脚本则在 init.zygoteXX.rc 中定义,这里拿 64 位处理器为例, init.zygote64.rc 的代码如下所示:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

       其中service用于通知init进程创建名zygote的进程,这个zygote进程执行程序的路径为/system/bin/app_process64,后面的则是要传给app_process64的参数。class main指的是zygote的class name为main,后文会用到它。

    4 解析 Service 类型语句

       接下来解析 Service ,前面有提到过,解析Service语句采用ServiceParse来进行判断, Service Parser 的实现代码在system/core/init/service.cpp中,它会用到两个函数,一个是ParseSection,  它会解析Service的.rc文件, 比如上文讲到的 init.zygote64.rc, ParseSection 函数 主要用来搭建 Service 的架子; 另一 个是 ParseLineSection ,用于解析子项,service.cpp的 代码如下所示:
system/core/init/service.cpp
bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
    if (args.size() < 3) {
        // 判断Services 是否有name 和 program(可执行程序)
        *err = "services must have a name and a program";
        return false;
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {
        // 检查Service的name是否有效
        *err = StringPrintf("invalid service name '%s'", name.c_str());
        return false;
    }

    // 在service_manager中查找有没有名字为name的Service
    Service* old_service = service_manager_->FindServiceByName(name);
    if (old_service) {
        // 判断是否存在重名的Service
        *err = "ignored duplicate definition of service '" + name + "'";
        return false;
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());
    service_ = std::make_unique<Service>(name, str_args); // ... 1
    return true;
}


bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
    return service_ ? service_->ParseLine(std::move(args), err) : false;
}
       注释1 处,根据参数构造出一个 Service 对象,它的 class name为变量name的值 。在解析完 所有数据后,会调用 EndSection 函数。
system/core/init/service.cpp
void ServiceParser::EndSection() {
    if (service_) {
        service_manager_->AddService(std::move(service_));
    }
}
       End Se ction 函数中会调用 Servi ceManage的 AddService 函数,接着查看 Add Servi ce函数做了什么,代码如下:
system/core/init/service.cpp
void ServiceManager::AddService(std::unique_ptr<Service> service) {
    services_.emplace_back(std::move(service)); // ... 1
}
       注释1 处的代码将 Service 对象加入 Servi ce 链表中 。上 面的 Service 解析过程总体来讲就是根据参数创建出 Service 对象,然后根据选项域的内容填充 Servi ce 对象,最后将 Service 对象加入 vect or 类型的 Service 链表中。  

    5 init 启动 Zygote

       讲完了解析 Service ,接下来讲 in it 是如何启动 Service 的,在这里主要讲解启动 Zygote 这个 Servi ce 。在 Zygote 的启动脚本中,我们可知 Zygote class name 为 main 。在 init.rc 中有如下配置代码:
system/core/rootd ir/init. re
...
import /init.${ro.zygote}.rc
...
on nonencrypted
    class_start main
    class_start late_start

       其中class_start是一个command,对应的函数为do_class_start。我们知道main指的就是Zygote,因此class_start main用来启动Zygote。do_class_start函数在builtins.cpp中定义,如下所示。

system/core/init/builtins.cpp

static int do_class_start(const std::vector<std::string>& args) {
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    ServiceManager::GetInstance().
        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
    return 0;
}

      ForEachServicelnClass 函数会遍历 Service 链表,找到 class name 为main的Zygote ,并执行 StartlfNotDisabled 函数,如下所示:

system/core/init/service.cpp

bool Service::StartIfNotDisabled() {
    if (!(flags_ & SVC_DISABLED)) { // ... 1
        return Start();
    } else {
        flags_ |= SVC_DISABLED_START;
    }
    return true;
}
       注释1 处,如果 Service 没有在其对应的 rc 文件中设 disa bled 选项,则会 调用 Start 函数启动该 Serv ice, Zygote 对应的 ini t.zygote64 .rc  中并没有设置 di sa bled 选项 ,因 此我们 接着来查看 Sta rt 函数,如下所示

system/core/init/service.cpp

bool Service::Start() {
    // 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.
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));

    // 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 (flags_ & SVC_RUNNING) {
        return false;
    }

    // 是否需要控制台环境
    bool needs_console = (flags_ & SVC_CONSOLE);
    if (needs_console) {
        if (console_.empty()) {
            console_ = default_console;
        }

        // Make sure that open call succeeds to ensure a console driver is
        // properly registered for the device node
        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
        if (console_fd < 0) {
            PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
            flags_ |= SVC_DISABLED;
            return false;
        }
        close(console_fd);
    }

    //判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
        flags_ |= SVC_DISABLED;
        return false;
    }

    std::string scon;
    if (!seclabel_.empty()) {
        scon = seclabel_;
    } else {
        scon = ComputeContextFromExecutable(name_, args_[0]);
        if (scon == "") {
            return false;
        }
    }

    LOG(INFO) << "starting service '" << name_ << "'...";

    //1 如果子进程没有启动,则调用 fork 函数创建子进程
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork(); //重点,通过fock来创建子进程
    }

    if (pid == 0) {  //2 运行在子进程中
        umask(077);

        if (namespace_flags_ & CLONE_NEWPID) {
            // This will fork again to run an init process inside the PID
            // namespace.
            SetUpPidNamespace(name_);
        }

        for (const auto& ei : envvars_) {
            add_environment(ei.name.c_str(), ei.value.c_str());
        }

        std::for_each(descriptors_.begin(), descriptors_.end(),
                      std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));

        // See if there were "writepid" instructions to write to files under /dev/cpuset/.
        auto cpuset_predicate = [](const std::string& path) {
            return StartsWith(path, "/dev/cpuset/");
        };
        auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
        if (iter == writepid_files_.end()) {
            // There were no "writepid" instructions for cpusets, check if the system default
            // cpuset is specified to be used for the process.
            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
            if (!default_cpuset.empty()) {
                // Make sure the cpuset name starts and ends with '/'.
                // A single '/' means the 'root' cpuset.
                if (default_cpuset.front() != '/') {
                    default_cpuset.insert(0, 1, '/');
                }
                if (default_cpuset.back() != '/') {
                    default_cpuset.push_back('/');
                }
                writepid_files_.push_back(
                    StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
            }
        }
        std::string pid_str = std::to_string(getpid());
        for (const auto& file : writepid_files_) {
            if (!WriteStringToFile(pid_str, file)) {
                PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
            }
        }

        if (ioprio_class_ != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
                PLOG(ERROR) << "failed to set pid " << getpid()
                            << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
            }
        }

        if (needs_console) {
            setsid();
            OpenConsole();
        } else {
            ZapStdio();
        }

        // As requested, set our gid, supplemental gids, uid, context, and
        // priority. Aborts on failure.
        SetProcessAttributes();

        if (!ExpandArgsAndExecve(args_)) {  //3 重点,开启子进程执行。
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    }

    if (pid < 0) {
        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
        pid_ = 0;
        return false;
    }

    if (oom_score_adjust_ != -1000) {
        std::string oom_str = std::to_string(oom_score_adjust_);
        std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
        if (!WriteStringToFile(oom_str, oom_file)) {
            PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
        }
    }

    time_started_ = boot_clock::now();
    pid_ = pid;
    flags_ |= SVC_RUNNING;
    process_cgroup_empty_ = false;

    errno = -createProcessGroup(uid_, pid_);
    if (errno != 0) {
        PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                    << name_ << "'";
    } else {
        if (swappiness_ != -1) {
            if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
                PLOG(ERROR) << "setProcessGroupSwappiness failed";
            }
        }

        if (soft_limit_in_bytes_ != -1) {
            if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
                PLOG(ERROR) << "setProcessGroupSoftLimit failed";
            }
        }

        if (limit_in_bytes_ != -1) {
            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
                PLOG(ERROR) << "setProcessGroupLimit failed";
            }
        }
    }

    if ((flags_ & SVC_EXEC) != 0) {
        LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
                  << supp_gids_.size() << " context "
                  << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
    }

    NotifyStateChange("running");
    return true;
}

       在Start函数中,首先判断Service是否已经运行,如果已经运行了就不会再启动,直接返回false。当程序执行到注释1处,说明子进程还没有被启动,调用fork函数来创建子进程,并返回pid值看,在注释2处如果pid=0,则说明当前代码逻辑执行在子进程中,注释3处在子进程中调用ExpandArgsAndExecve函数,接下来看看ExpandArgsAndExecve函数做了什么事情,代码如下:

static bool ExpandArgsAndExecve(const std::vector<std::string>& args) {
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;

    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
        if (!expand_props(args[i], &expanded_args[i])) {
            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
        }
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    return execve(c_strings[0], c_strings.data(), (char**)ENV) == 0; // ... 1
}

       在ExpandArgsAndExecve函数中,调用注释1处的execve函数,Service子进程就会被启动,并进入该Service的main函数,如果该Service是Zygote,上文中有提到过Zygote的执行路径为system/bin/app_process64,这样就会进入frameworks/base/cmds/app_process/App_main. cpp的main函数,也就是Zygote的main函数,代码如下:

frameworks/base/cmds/app_process/App_main. cpp
int main(int argc, char* const argv[])
{
    ....
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // ... 1
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}
       从注释1 处的代码可以得知,调用 runtime的 start 函数启动 Zygote ,至此 Zygote 就启动了

    6 属性服务

       init 进程 启动时会 启动属性服务,并为其分配 存,用来存储这些属性,如 果需要这些 属性直接读取就可 以了, 1.2 节的开头部分,我 们提到 init.cpp main 函数中与属性务相关的 代码 以下两行:
system/core/init/init.cpp
    // 初始化属性服务
    property_init(); 

    // 启动属性服务
    start_property_service();  

       这两行代码用来初始化属性服务配置并启动属性服务。 首先我们来学习属性服务配置的初始化和启动。

      1. 属性服务初始化与启动

       property_ init 函数的具体实现代码如下所示:
system/core/init/Property_service. cpp
void property_init() {
    if (__system_property_area_init()) {
        LOG(ERROR) << "Failed to initialize property area";
        exit(1);
    }
}

__system_property_area_init函数用来初始化属性内存区域。接下来查看start_property_service函数的具体代码:

system/core/init/Property_service. cpp
void start_property_service() {
    property_set("ro.property_service.version", "2");

    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr, sehandle); // ... 1
    if (property_set_fd == -1) {
        PLOG(ERROR) << "start_property_service socket creation failed";
        exit(1);
    }

    listen(property_set_fd, 8); // ... 2

    register_epoll_handler(property_set_fd, handle_property_set_fd); // ... 3
}

       在注释1处创建非阻塞的 Socket。在注释 2处调用 listen 数对 property_set_fd进行监听,这样创建的 Socket 就成为 server ,也就是属性服务; listen 函数的第二个参数设置为 8, 表示属性服务最多可以同时为8个试图设置属性的用户提供服务。注释3处的代码将 property_set_fd放入了epoll 中,用 epoll 来监听 property_set_fd :当 property_set_fd中有数据到来时, init 进程将调用 handle_property_set_fd 函数进行处理。

       在Linux 新的内核中, epoll 用来替换 select, epoll是 Linux 内核为处理大批量文件描述符而做了改进的 poll ,是Linux下多路复用 I/0 接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下系统 CPU资源的利用率。 epoll 内部用于保存事件的数据类型是红黑树,查找速度快, select 采用的数组保存信息,查找速度很慢,只有当等待少量文件描述符时, epoll和select 效率才会差不多。

      2. 服务处理客户端请求

       从上面我们得知, 属性服务接收到客户端 请求时,会调用 handle_property_set_fd数进行处理:

system/core/init/Property_service.cpp

static void handle_property_set_fd() {
    
    ...

    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];
        // 如果socket读取不到属性数据直接返回。
        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        handle_property_set(socket, prop_value, prop_value, true); // ... 1
        break;
      }

      ...     

    }
}

       在注释1处说明handle_property_set_fd数通过调用handle_property_set函数来处理客户端请求,代码如下:

system/core/init/Property_service.cpp

static void handle_property_set(SocketConnection& socket,
                                const std::string& name,
                                const std::string& value,
                                bool legacy_protocol) {
  ...

  // 以"ctl. "开头,表示是控制属性
  if (android::base::StartsWith(name, "ctl.")) { // ... 1
    // 检查客户端权限
    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
      // 设置控制属性
      handle_control_message(name.c_str() + 4, value.c_str()); // ... 2
      if (!legacy_protocol) {
        socket.SendUint32(PROP_SUCCESS);
      }
    } else {
      LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
                 << " service ctl [" << value << "]"
                 << " uid:" << cr.uid
                 << " gid:" << cr.gid
                 << " pid:" << cr.pid;
      if (!legacy_protocol) {
        socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
      }
    }
  } else {
    // 非控制属性,普通属性,
    // 检查客户端权限
    if (check_mac_perms(name, source_ctx, &cr)) {
      uint32_t result = property_set(name, value); // ... 3
      if (!legacy_protocol) {
        socket.SendUint32(result);
      }
    } else {
      LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
      if (!legacy_protocol) {
        socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
      }
    }
  }

  freecon(source_ctx);
}

        由以上代码可以知道,系统属性分为两种类型:一种是普通属性,还有一种是控制属性。控制属性用来执行一些命令 ,比如开机的动画就使用了这种属性。 注释1处的属性名称以“ctl.”开头,就说明是控制属性,然后调用 handle_control_message 函数来修改控制属性。如果是普通属性,调用注释3处的 property_set 函数来对普通属性进行修改,如下所示:

system/core/init/Property_service.cpp

uint32_t property_set(const std::string& name, const std::string& value) {
    if (name == "selinux.restorecon_recursive") {
        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
    }

    return PropertySetImpl(name, value); // ... 1
}

      在property_set方法中,首先判断属性的名字是否为“selinux.restorecon_recursive”,若果是则使用PropertySetAsync函数完成设置,如果不是则使用PropertySetImpl函数完成设置,这里我们分析PropertySetImpl函数完成属性设置,代码如下:

system/core/init/Property_service.cpp

static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
    size_t valuelen = value.size();
    // 判断属性的名字是否合法
    if (!is_legal_property_name(name)) {
        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
        return PROP_ERROR_INVALID_NAME;
    }

    if (valuelen >= PROP_VALUE_MAX) {
        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                   << "value too long";
        return PROP_ERROR_INVALID_VALUE;
    }

    // 从属性存储空间查找该属性
    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    // 判断属性是否存在
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
        // 如果属性名称以"ro."开头,表示只读属性,不能修改,直接返回。
        if (android::base::StartsWith(name, "ro.")) {
            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                       << "property already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }
        // 如果属性存在,更新属性值。
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
        // 如果属性不存在,添加该属性
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                       << "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    // 对"persist."开头的属性进行处理
    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
        write_persistent_property(name.c_str(), value.c_str());
    }
    property_changed(name, value);
    return PROP_SUCCESS;
}
       PropertySetImpl 函数主要对普通属性进行修改,首先要判断该属性是否合法,如果合法就从属性存储空间中查找该属性,如果属性存在,就更新属性值,否则就添加该属性。另外,还对名称以“ ro.”“ persist . ”开头的属性进行了相应的处理。

    7 init 进程启动总结

       init 进 程启动做了很多的工作,总的来说主要做了以下三件事:
       ( 1)   创建和挂载启动所需 的文件目 录。
       (2 )初始化和启动属性服务。
       (3 )解析 init.rc 配置文件并启动 zygote 进程。
 

猜你喜欢

转载自blog.csdn.net/lixiong0713/article/details/106648080