zoukankan      html  css  js  c++  java
  • Android系统启动流程(一)解析init进程启动过程

    前言

    作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程。

    1.init简介

    init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init。本文将基于Android7.0源码来分析Init进程。

    2.引入init进程

    说到init进程,首先要提到Android系统启动流程的前几步:
    1.启动电源以及系统启动
    当电源按下时引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM,然后执行。
    2.引导程序Bootloader
    引导程序是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。
    3.linux内核启动
    内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
    4.init进程启动

    讲到第四步就发现我们这一节要讲的init进程了。关于Android系统启动流程的所有步骤会在本系列的最后一篇做讲解。

    3.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);
        }
        umask(0);
        add_environment("PATH", _PATH_DEFPATH);
        bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
        //创建文件并挂载
        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);
            #define MAKE_STR(x) __STRING(x)
            mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
            mount("sysfs", "/sys", "sysfs", 0, NULL);
        }
        open_devnull_stdio();
        klog_init();
        klog_set_level(KLOG_NOTICE_LEVEL);
        NOTICE("init %s started!
    ", is_first_stage ? "first stage" : "second stage");
        if (!is_first_stage) {
            // Indicate that booting is in progress to background fw loaders, etc.
            close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
            //初始化属性相关资源
            property_init();//1
            process_kernel_dt();
            process_kernel_cmdline();
            export_kernel_boot_props();
        }
     ...
        //启动属性服务
        start_property_service();//2
        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>());
        //解析init.rc配置文件
        parser.ParseConfig("/init.rc");//3
       ...   
           while (true) {
            if (!waiting_for_exec) {
                am.ExecuteOneCommand();
                restart_processes();
            }
            int timeout = -1;
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
            if (am.HasMoreCommands()) {
                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
    ", strerror(errno));
            } else if (nr == 1) {
                ((void (*)()) ev.data.ptr)();
            }
        }
        return 0;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    init的main方法做了很多事情,我们只需要关注主要的几点,在注释1处调用 property_init来对属性进行初始化并在注释2处的 调用start_property_service启动属性服务,关于属性服务,后面会讲到。注释3处 parser.ParseConfig(“/init.rc”)用来解析init.rc。解析init.rc的文件为system/core/init/init_parse.cpp文件,接下来我们查看init.rc里做了什么。

    4.init.rc

    init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,它主要包含五种类型语句:
    Action、Commands、Services、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
        # set RLIMIT_NICE to allow priorities from 19 to -20
        setrlimit 13 40 40
    ...    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

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

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

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

    需要注意的是在Android 7.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
        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
        writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    5.解析service

    接下来我们来解析service,会用到两个函数,一个是ParseSection,它会解析service的rc文件,比如上文讲到的init.zygote64.rc,ParseSection函数主要用来搭建service的架子。另一个是ParseLineSection,用于解析子项。代码如下所示。
    system/core/init/service.cpp

    bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                     std::string* err) {
        if (args.size() < 3) {
            *err = "services must have a name and a program";
            return false;
        }
        const std::string& name = args[1];
        if (!IsValidName(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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注释1处,根据参数,构造出一个service对象,它的classname为”default”。当解析完毕时会调用EndSection:

    void ServiceParser::EndSection() {
        if (service_) {
            ServiceManager::GetInstance().AddService(std::move(service_));
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    接着查看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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    6.init启动zygote

    讲完了解析service,接下来该讲init是如何启动service,在这里我们主要讲解启动zygote这个service。在zygote的启动脚本中我们得知zygote的class name为main。在init.rc有如下配置代码:
    system/core/rootdir/init.rc

    ...
    on nonencrypted    
        # A/B update verifier that marks a successful boot.  
        exec - root -- /system/bin/update_verifier nonencrypted  
        class_start main         
        class_start late_start 
    ...    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中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;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    来查看StartIfNotDisabled做了什么:
    system/core/init/service.cpp

    bool Service::StartIfNotDisabled() {
        if (!(flags_ & SVC_DISABLED)) {
            return Start();
        } else {
            flags_ |= SVC_DISABLED_START;
        }
        return true;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接着查看Start方法,如下所示。

    bool Service::Start() {
        flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
        time_started_ = 0;
        if (flags_ & SVC_RUNNING) {//如果Service已经运行,则不启动
            return false;
        }
        bool needs_console = (flags_ & SVC_CONSOLE);
        if (needs_console && !have_console) {
            ERROR("service '%s' requires console
    ", name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
      //判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
        struct stat sb;
        if (stat(args_[0].c_str(), &sb) == -1) {
            ERROR("cannot find '%s' (%s), disabling '%s'
    ",
                  args_[0].c_str(), strerror(errno), name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
    
    ...
        pid_t pid = fork();//1.fork函数创建子进程
        if (pid == 0) {//运行在子进程中
            umask(077);
            for (const auto& ei : envvars_) {
                add_environment(ei.name.c_str(), ei.value.c_str());
            }
            for (const auto& si : sockets_) {
                int socket_type = ((si.type == "stream" ? SOCK_STREAM :
                                    (si.type == "dgram" ? SOCK_DGRAM :
                                     SOCK_SEQPACKET)));
                const char* socketcon =
                    !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
    
                int s = create_socket(si.name.c_str(), socket_type, si.perm,
                                      si.uid, si.gid, socketcon);
                if (s >= 0) {
                    PublishSocket(si.name, s);
                }
            }
    ...
            //2.通过execve执行程序
            if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s
    ", args_[0].c_str(), strerror(errno));
            }
    
            _exit(127);
        }
    ...
        return true;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    通过注释1和2的代码,我们得知在Start方法中调用fork函数来创建子进程,并在子进程中调用execve执行system/bin/app_process,这样就会进入framework/cmds/app_process/app_main.cpp的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.
    ");
            app_usage();
            LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
            return 10;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    从注释1处的代码可以得知调用runtime(AppRuntime)的start来启动zygote。

    7.属性服务

    Windows平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,它还是能够根据之前在注册表中的记录,进行相应的初始化工作。Android也提供了一个类似的机制,叫做属性服务。
    在本文的开始,我们提到在init.cpp代码中和属性服务相关的代码有:
    system/core/init/init.cpp

      property_init();
      start_property_service();
    • 1
    • 2

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

    属性服务初始化与启动

    property_init函数具体实现的代码如下所示。
    system/core/init/property_service.cpp

    void property_init() {
        if (__system_property_area_init()) {
            ERROR("Failed to initialize property area
    ");
            exit(1);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    void start_property_service() {
        property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                        0666, 0, 0, NULL);//1
        if (property_set_fd == -1) {
            ERROR("start_property_service socket creation failed: %s
    ", strerror(errno));
            exit(1);
        }
        listen(property_set_fd, 8);//2
        register_epoll_handler(property_set_fd, handle_property_set_fd);//3
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注释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最大的好处在于它不会随着监听fd数目的增长而降低效率。因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

    属性服务处理请求
    从上文我们得知,属性服务接收到客户端的请求时,会调用handle_property_set_fd函数进行处理:
    system/core/init/property_service.cpp

    static void handle_property_set_fd()
    {  
    ...
    
            if(memcmp(msg.name,"ctl.",4) == 0) {
                close(s);
                if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
                    handle_control_message((char*) msg.name + 4, (char*) msg.value);
                } else {
                    ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d
    ",
                            msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
                }
            } else {
                //检查客户端进程权限
                if (check_mac_perms(msg.name, source_ctx, &cr)) {//1
                    property_set((char*) msg.name, (char*) msg.value);//2
                } else {
                    ERROR("sys_prop: permission denied uid:%d  name:%s
    ",
                          cr.uid, msg.name);
                }
                close(s);
            }
            freecon(source_ctx);
            break;
        default:
            close(s);
            break;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    注释1处的代码用来检查客户端进程权限,在注释2处则调用property_set函数对属性进行修改,代码如下所示。

    int property_set(const char* name, const char* value) {
        int rc = property_set_impl(name, value);
        if (rc == -1) {
            ERROR("property_set("%s", "%s") failed
    ", name, value);
        }
        return rc;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    property_set函数主要调用了property_set_impl函数:

    static int property_set_impl(const char* name, const char* value) {
        size_t namelen = strlen(name);
        size_t valuelen = strlen(value);
        if (!is_legal_property_name(name, namelen)) return -1;
        if (valuelen >= PROP_VALUE_MAX) return -1;
        if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
            if (selinux_reload_policy() != 0) {
                ERROR("Failed to reload policy
    ");
            }
        } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
            if (restorecon_recursive(value) != 0) {
                ERROR("Failed to restorecon_recursive %s
    ", value);
            }
        }
        //从属性存储空间查找该属性
        prop_info* pi = (prop_info*) __system_property_find(name);
        //如果属性存在
        if(pi != 0) {
           //如果属性以"ro."开头,则表示是只读,不能修改,直接返回
            if(!strncmp(name, "ro.", 3)) return -1;
           //更新属性值
            __system_property_update(pi, value, valuelen);
        } else {
           //如果属性不存在则添加该属性
            int rc = __system_property_add(name, namelen, value, valuelen);
            if (rc < 0) {
                return rc;
            }
        }
        /* If name starts with "net." treat as a DNS property. */
        if (strncmp("net.", name, strlen("net.")) == 0)  {
            if (strcmp("net.change", name) == 0) {
                return 0;
            }
          //以net.开头的属性名称更新后,需要将属性名称写入net.change中  
            property_set("net.change", name);
        } else if (persistent_properties_loaded &&
                strncmp("persist.", name, strlen("persist.")) == 0) {
            /*
             * Don't write properties to disk until after we have read all default properties
             * to prevent them from being overwritten by default values.
             */
            write_persistent_property(name, value);
        }
        property_changed(name, value);
        return 0;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    property_set_impl函数主要用来对属性进行修改,并对以ro、net和persist开头的属性进行相应的处理。到这里,属性服务处理请求的源码就讲到这。

    8.init进程总结

    讲到这,总结起来init进程主要做了三件事:
    1.创建一些文件夹并挂载设备
    2.初始化和启动属性服务
    3.解析init.rc配置文件并启动zygote进程

    参考资料:
    《深入理解Android系统》
    《深入理解Android卷I》
    Android的init过程详解(一)
    Android启动过程深入解析
    Android7.0解析Init.rc文件
    Android 7.0 init.rc的一点改变
    Android7.0 init进程源码分析
    Android情景分析之属性服务


  • 相关阅读:
    241. Different Ways to Add Parentheses java solutions
    89. Gray Code java solutions
    367. Valid Perfect Square java solutions
    46. Permutations java solutions
    116. Populating Next Right Pointers in Each Node java solutions
    153. Find Minimum in Rotated Sorted Array java solutions
    判断两颗树是否相同
    求二叉树叶子节点的个数
    求二叉树第k层的结点个数
    将二叉排序树转换成排序的双向链表
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7965946.html
Copyright © 2011-2022 走看看