美文网首页
Android系统启动(一)init进程启动过程

Android系统启动(一)init进程启动过程

作者: Eren丶耶格尔 | 来源:发表于2019-02-05 20:54 被阅读0次

    1. init简介

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

    2. 引入init进程

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

    1. 启动电源以及系统启动

    当电源按下时引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM,然后执行。

    2. 引导程序Bootloader

    引导程序是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。

    3. linux内核启动

    内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init.rc”文件,并启动init进程。

    4. init进程启动

    init进程做的工作比较多,主要用来初始化和启动属性服务,也用来启动Zygote进程。

    3. init进程的入口函数

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

    int main(int argc, char** argv) {
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);
        }
    
        if (!strcmp(basename(argv[0]), "watchdogd")) {
            return watchdogd_main(argc, argv);
        }
    
        if (REBOOT_BOOTLOADER_ON_PANIC) {
            install_reboot_signal_handlers();
        }
    
        add_environment("PATH", _PATH_DEFPATH);
    
        bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
    
        if (is_first_stage) {
            boot_clock::time_point start_time = boot_clock::now();
    
            // 清理 umask
            umask(0);
    
            // 创建和挂载启动所需的文件目录
            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));
            // 不要将原始命令行暴露给非特权进程
            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));
    
            // 初始化 Kernel的Log,这样就可以从外界获取Kernel的日志
            InitKernelLogging(argv);
            ···
    }
    ···
    // 对属性服务进行初始化
    property_init();  //1
    ···
    // 创建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
        if (epoll_fd == -1) {
            PLOG(ERROR) << "epoll_create1 failed";
            exit(1);
        }
        // 用于设置子进程信号处理函数,如果子进程(Zygote)异常退出,init进程会调用该函数中设定的信号处理函数来进行处理
        signal_handler_init();  //2
        // 导入默认的环境变量
        property_load_boot_defaults();
        export_oem_lock_status();
        // 启动属性服务
        start_property_service();  //3
        set_usb_controller();
    
        const BuiltinFunctionMap function_map;
        Action::set_function_map(&function_map);
    
        Parser& parser = Parser::GetInstance();
        parser.AddSectionParser("service",std::make_unique<ServiceParser>());
        parser.AddSectionParser("on", std::make_unique<ActionParser>());
        parser.AddSectionParser("import", std::make_unique<ImportParser>());
        std::string bootscript = GetProperty("ro.boot.init_rc", "");
        if (bootscript.empty()) {
            //解析init.rc配置文件
            parser.ParseConfig("/init.rc");  //4
            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);
        }
    
        if (false) parser.DumpState();
    
        ActionManager& am = ActionManager::GetInstance();
    ···
    while (true) {
            // By default, sleep until something happens.
            int epoll_timeout_ms = -1;
    
            if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
                // 内部遍历执行每个action中携带的command对应的执行函数
                am.ExecuteOneCommand();
            }
            if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
                //重启死去的进程
                restart_processes();  //5
    
                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 (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函数做了很多事情,比较复杂,我们只需关注主要的几点就可以了。在开始的时候创建和挂载启动所需的文件目录,其中挂载了 tmpfs、devpts、proc、sysfs 和 selinuxfs 共5种文件系统,这些都是系统运行时目录,顾名思义,只在系统运行时才会存在,系统停止时会消失。

    在注释 1 处调用 property_init 函数来对属性进行初始化,并在注释 3 处调用 start_property_service 函数启动属性服务。在注释 2 处调用 signal_handler_init 函数用于设置子进程信号处理函数,它被定义在 system/core/init/signal_handler.cpp 中,主要用于防止init进程的子进程成为僵尸进程,为了防止僵尸进程的出现,系统会在子进程暂停和终止的时候发出SIGCHLD信号,而signal_handler_init函数就是用来接收SIGCHLD信号的(其内部只处理进程终止的SIGCHLD信号)。

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

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

    僵尸进程与危害

    在UNIX/Linux中,父进程使用fork创建子进程,在子进程终止之后,如果父进程并不知道子进程已经终止了,这时子进程虽然已经退出了,但是在系统进程表中还为它保留了一定的信息(比如进程号、退出状态、运行时间等),这个子进程就被称作僵尸进程。系统进程表是一项有限资源,如果系统进程表被僵尸进程耗尽的话,系统就可能无法创建新的进程了。

    4. 解析 init.rc

    init.rc 视一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,这种语言主要包含5种类型语句:Action、Command、Service、Option 和 Improt。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
        # set RLIMIT_NICE to allow priorities from 19 to -20
        setrlimit 13 40 40
    ...
    

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

    on <trigger> [&& <trigger>]*     //设置触发器  
       <command>  
       <command>      //动作触发之后要执行的命令
    

    为了分析如何创建zygote,我们主要查看Services类型语句,它的格式如下所示:

    service <name> <pathname> [ <argument> ]*   //<service的名字><执行程序路径><传递参数>  
       <option>       //option是service的修饰词,影响什么时候、如何启动services  
       <option>  
       ...
    

    需要注意的是,Android8.0中对init.rc文件进行了拆分,每个服务对应一个rc文件。我们要分析的Zygote启动脚本则在init.zygoteXX.rc中定义,这里拿64位处理器为例,init.zygote64.rc的代码如下所示:
    system/core/rootdir/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,后文会用到它。

    5. 解析service类型语句

    init.rc中的Action类型语句和Service类型语句都有相应的类来进行解析,Action类型语句采用ActionParser来进行解析,Service类型语句采用ServiceParser来进行解析,这里因为主要分析Zygote,所以只介绍ServiceParser。ServiceParser的实现代码在system/core/init/service.cpp中,接下来我们来查看ServiceParser是如何解析上面提到的Service类型语句的,会用到两个函数:一个是ParseSection,它会解析Service的rc文件,比如上文讲到的init.zygote64.rc,ParseSetion函数主要用来搭建Service的架子;另一个是PareLineSection,用于解析子项。代码如下所示:
    system/core/init/service.cpp

    bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                     std::string* err) {
        if (args.size() < 3) {  //判断Service是否有name与执行程序
            *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;
        }
        std::vector<std::string> str_args(args.begin() + 2, args.end());
        service_ = std::make_unique<Service>(name, "default", str_args);  //1
        return true;
    }
    
    bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                         const std::string& filename, int line,
                                         std::string* err) const {
        return service_ ? service_->HandleLine(args, err) : false;
    }
    

    注释 1 处,根据参数,构造出一个Service对象,它的classname为default。在解析完所有数据后,会调用EndSection:

    void ServiceParser::EndSection() {
        if (service_) {
            ServiceManager::GetInstance().AddService(std::move(service_));
        }
    }
    

    EndSection函数中会调用ServiceManager的AddService函数,接着查看AddService做了什么:

    void ServiceManager::AddService(std::unique_ptr<Service> service) {
        Service* old_service = FindServiceByName(service->name());
        if (old_service) {
            ERROR("ignored duplicate definition of service '%s'",
                  service->name().c_str());
            return;
        }
        services_.emplace_back(std::move(service)); //1
    }
    

    注释 1 处的代码将service对象加入到services链表中。上面的解析过程总体来讲就是根据参数创建出service对象,然后根据选项域的内容填充service对象,最后将service对象加入到vector类型的services链表中。

    6. init启动Zygote

    相关文章

      网友评论

          本文标题:Android系统启动(一)init进程启动过程

          本文链接:https://www.haomeiwen.com/subject/umzxtqtx.html