美文网首页
Android系统源码解析-init进程启动过程

Android系统源码解析-init进程启动过程

作者: Battery_fc | 来源:发表于2019-04-24 08:31 被阅读0次

    一,简介

    init 进程是Android 用户空间的第一个进程,被赋予了很多职责,比如启动 zygote(孵化器),启动属性服务等。相关源码位于system/core/init 目录下。
    在分析init进程启动过程前,需要清楚电源按下时,一系列流程:

    1,电源按下时,引导芯片的代码开始从预定义的地方(ROM)中开始执行,加载引导程序bootloader到RAM,然后执行。
    2,bootloader 是 系统运行前的一个小程序,它负责把 系统OS拉起来并运行
    3,liunx 内核开始启动,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系
    统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
    4,此时,init进程开始启动,本文分析的主要内容。

    2,源码解析

    init入口函数为init.cpp 的mian 函数,源码目录为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);
        }
    
        // Clear the umask.
        umask(0);
    
        add_environment("PATH", _PATH_DEFPATH);
    
        bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 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.
        //挂载文件
        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);
        }
    
        // We must have some place other than / to create the device nodes for
        // kmsg and null, otherwise we won't be able to remount / read-only
        // later on. Now that tmpfs is mounted on /dev, we can actually talk
        // to the outside world.
        open_devnull_stdio();
        klog_init();
        klog_set_level(KLOG_NOTICE_LEVEL);
    
        NOTICE("init %s started!\n", 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
    
            // 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();
        }
    
        // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
        selinux_initialize(is_first_stage);
    
        // If we're in the kernel domain, re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (is_first_stage) {
            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 };
            if (execv(path, args) == -1) {
                ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
                security_failure();
            }
        }
    
        // These directories were necessarily created before initial policy load
        // and therefore need their security context restored to the proper value.
        // This must happen before /dev is populated by ueventd.
        NOTICE("Running restorecon...\n");
        restorecon("/dev");
        restorecon("/dev/socket");
        restorecon("/dev/__properties__");
        restorecon("/property_contexts");
        restorecon_recursive("/sys");
    
        epoll_fd = epoll_create1(EPOLL_CLOEXEC);
        if (epoll_fd == -1) {
            ERROR("epoll_create1 failed: %s\n", strerror(errno));
            exit(1);
        }
    
        signal_handler_init();
    
        property_load_boot_defaults();
        export_oem_lock_status();
        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>());
        parser.ParseConfig("/init.rc");//3
         ...
            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;
    }
    

    注释1处,通过property_init()初始化属性相关的资源并通过注释2处的start_property_service()启动属性服务。注释3处,解析init.rc文件。先来看看属性服务是如何启动的。

    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\n", strerror(errno));
            exit(1);
        }
        listen(property_set_fd, 8);//2
        register_epoll_handler(property_set_fd, handle_property_set_fd);//3
    }
    

    注释1处创建socket,注释二处,通过listen函数对property_set_fd进行监听,这样创建的socker就变成了server,即属性服务。第二个参数8表示属性服务最多可以为8个试图同时设置属性的用户提供服务。注释3处把property_set_fd放入epoll句柄,当property_set_fd 有数据到来时,init进程会用handle_property_set_fd函数进行处理

    static void handle_property_set_fd()
    {
        ...
    
            if (!is_legal_property_name(msg.name, strlen(msg.name))) {
                ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
                close(s);
                return;
            }
    
            getpeercon(s, &source_ctx);
    
            if(memcmp(msg.name,"ctl.",4) == 0) {
                // Keep the old close-socket-early behavior when handling
                // ctl.* properties.
                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\n",
                            msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
                }
            } else {
                if (check_mac_perms(msg.name, source_ctx, &cr)) {
                    property_set((char*) msg.name, (char*) msg.value);//1
                } else {
                    ERROR("sys_prop: permission denied uid:%d  name:%s\n",
                          cr.uid, msg.name);
                }
    
                // Note: bionic's property client code assumes that the
                // property server will not close the socket until *AFTER*
                // the property is written to memory.
                close(s);
            }
            freecon(source_ctx);
            break;
    
        default:
            close(s);
            break;
        }
    }
    

    注释1处调用 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\n", name, value);
        }
        return rc;
    }
    

    继续调用 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\n");
            }
        } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
            if (restorecon_recursive(value) != 0) {
                ERROR("Failed to restorecon_recursive %s\n", value);
            }
        }
    
      //查找属性的名字
        prop_info* pi = (prop_info*) __system_property_find(name);
    
        if(pi != 0) {//找到了
            /* ro.* properties may NEVER be modified once set */
          //如果属性的名字是以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;
            }
           /*
            * The 'net.change' property is a special property used track when any
            * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
            * contains the last updated 'net.*' property.
            */
          //如果属性是以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;
    }
    

    property_set_impl 函数主要是修改属性,并对ro.net.和persist.开头的属性做了相应的处理。属性服务分析到这里,下面来看看 上面说的init.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
    

    上方代码表示 进程名为 zygote ,这个zygote 进程 执行程序的路径为/system/bin/app_process64,后面则为 传给app_process64的参数。class main 表示 zygote 进程的class name 为 main。该service 是如何启动的呢? 在system/core/rootdir/init.rc 文件中,有如下配置代码

    on nonencrypted
        # A/B update verifier that marks a successful boot.
        exec - root cache -- /system/bin/update_verifier nonencrypted
        class_start main//1
        class_start late_start
    

    注释1处,表明启动class name 为main 的 服务。上面提到了 zygote 进程 的class name 就为main。class_start 为一个COMMAND,对应的函数为 do_class_start,位于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;
    }
    

    来看看 s->StartIfNotDisabled(); 函数做了什么,位于system/core/init/service.cpp文件中

    bool Service::StartIfNotDisabled() {
        if (!(flags_ & SVC_DISABLED)) {
            return Start();
        } else {
            flags_ |= SVC_DISABLED_START;
        }
        return true;
    }
    

    如果允许启动,则调用Start 函数

    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));
        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 (flags_ & SVC_RUNNING) {//如果该服务正在运行,则不启动
            return false;
        }
    
        bool needs_console = (flags_ & SVC_CONSOLE);
        if (needs_console && !have_console) {
            ERROR("service '%s' requires console\n", name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
    
        struct stat sb;
        if (stat(args_[0].c_str(), &sb) == -1) {
            ERROR("cannot find '%s' (%s), disabling '%s'\n",
                  args_[0].c_str(), strerror(errno), name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
    
        std::string scon;
        if (!seclabel_.empty()) {
            scon = seclabel_;
        } else {
            char* mycon = nullptr;
            char* fcon = nullptr;
    
            INFO("computing context for service '%s'\n", args_[0].c_str());
            int rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", name_.c_str());
                return false;
            }
    
            rc = getfilecon(args_[0].c_str(), &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", name_.c_str());
                free(mycon);
                return false;
            }
    
            char* ret_scon = nullptr;
            rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
                                         &ret_scon);
            if (rc == 0) {
                scon = ret_scon;
                free(ret_scon);
            }
            if (rc == 0 && scon == mycon) {
                ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
                free(mycon);
                free(fcon);
                return false;
            }
            free(mycon);
            free(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", name_.c_str());
                return false;
            }
        }
    
        NOTICE("Starting service '%s'...\n", name_.c_str());
    
        pid_t pid = fork();//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);
                }
            }
    
            std::string pid_str = StringPrintf("%d", getpid());
            for (const auto& file : writepid_files_) {
                if (!WriteStringToFile(pid_str, file)) {
                    ERROR("couldn't write %s to %s: %s\n",
                          pid_str.c_str(), file.c_str(), strerror(errno));
                }
            }
    
            if (ioprio_class_ != IoSchedClass_NONE) {
                if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
                    ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                          getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
                }
            }
    
            if (needs_console) {
                setsid();
                OpenConsole();
            } else {
                ZapStdio();
            }
    
            setpgid(0, getpid());
    
            // As requested, set our gid, supplemental gids, and uid.
            if (gid_) {
                if (setgid(gid_) != 0) {
                    ERROR("setgid failed: %s\n", strerror(errno));
                    _exit(127);
                }
            }
            if (!supp_gids_.empty()) {
                if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
                    ERROR("setgroups failed: %s\n", strerror(errno));
                    _exit(127);
                }
            }
            if (uid_) {
                if (setuid(uid_) != 0) {
                    ERROR("setuid failed: %s\n", strerror(errno));
                    _exit(127);
                }
            }
            if (!seclabel_.empty()) {
                if (setexeccon(seclabel_.c_str()) < 0) {
                    ERROR("cannot setexeccon('%s'): %s\n",
                          seclabel_.c_str(), strerror(errno));
                    _exit(127);
                }
            }
    
            std::vector<std::string> expanded_args;
            std::vector<char*> strs;
            expanded_args.resize(args_.size());
            strs.push_back(const_cast<char*>(args_[0].c_str()));
            for (std::size_t i = 1; i < args_.size(); ++i) {
                if (!expand_props(args_[i], &expanded_args[i])) {
                    ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
                    _exit(127);
                }
                strs.push_back(const_cast<char*>(expanded_args[i].c_str()));
            }
            strs.push_back(nullptr);
    
            if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {//通过execve 执行程序
                ERROR("cannot execve('%s'): %s\n", strs[0], strerror(errno));
            }
    
            _exit(127);
        }
    
        if (pid < 0) {
            ERROR("failed to start '%s'\n", name_.c_str());
            pid_ = 0;
            return false;
        }
    
        time_started_ = gettime();
        pid_ = pid;
        flags_ |= SVC_RUNNING;
    
        if ((flags_ & SVC_EXEC) != 0) {
            INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
                 pid_, uid_, gid_, supp_gids_.size(),
                 !seclabel_.empty() ? seclabel_.c_str() : "default");
        }
    
        NotifyStateChange("running");
        return true;
    }
    

    在Start函数中,首先会判断该服务是否正在运行,如果正在运行,则直接返回,不重新启动。然后通过fork函数创建子进程,并在子进程中,通过execve 执行 /system/bin/app_process64 ,这样,就会执行 到 frameworks/base/cmd/app_process/app_main.cpp 中的main 函数中

    int main(int argc, char* const argv[])
    {
        if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
            // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
            // EINVAL. Don't die on such kernels.
            if (errno != EINVAL) {
                LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
                return 12;
            }
        }
    
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
        // Process command line arguments
        // ignore argv[0]
        argc--;
        argv++;
    
        // Everything up to '--' or first non '-' arg goes to the vm.
        //
        // The first argument after the VM args is the "parent dir", which
        // is currently unused.
        //
        // After the parent dir, we expect one or more the following internal
        // arguments :
        //
        // --zygote : Start in zygote mode
        // --start-system-server : Start the system server.
        // --application : Start in application (stand alone, non zygote) mode.
        // --nice-name : The nice name for this process.
        //
        // For non zygote starts, these arguments will be followed by
        // the main class name. All remaining arguments are passed to
        // the main method of this class.
        //
        // For zygote starts, all remaining arguments are passed to the zygote.
        // main function.
        //
        // Note that we must copy argument string values since we will rewrite the
        // entire argument block when we apply the nice name to argv0.
    
        int i;
        for (i = 0; i < argc; i++) {
            if (argv[i][0] != '-') {
                break;
            }
            if (argv[i][1] == '-' && argv[i][2] == 0) {
                ++i; // Skip --.
                break;
            }
            runtime.addOption(strdup(argv[i]));
        }
    
        // Parse runtime arguments.  Stop at first unrecognized option.
        bool zygote = false;
        bool startSystemServer = false;
        bool application = false;
        String8 niceName;
        String8 className;
    
        ++i;  // Skip unused "parent dir" argument.
        while (i < argc) {
            const char* arg = argv[i++];
            if (strcmp(arg, "--zygote") == 0) {
                zygote = true;
                niceName = ZYGOTE_NICE_NAME;
            } else if (strcmp(arg, "--start-system-server") == 0) {
                startSystemServer = true;
            } else if (strcmp(arg, "--application") == 0) {
                application = true;
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
                niceName.setTo(arg + 12);
            } else if (strncmp(arg, "--", 2) != 0) {
                className.setTo(arg);
                break;
            } else {
                --i;
                break;
            }
        }
    
    //封装args 参数
        Vector<String8> args;
        if (!className.isEmpty()) {
            // We're not in zygote mode, the only argument we need to pass
            // to RuntimeInit is the application argument.
            //
            // The Remainder of args get passed to startup class main(). Make
            // copies of them before we overwrite them with the process name.
            args.add(application ? String8("application") : String8("tool"));
            runtime.setClassNameAndArgs(className, argc - i, argv + i);
        } else {
            // We're in zygote mode.
            maybeCreateDalvikCache();
    
            if (startSystemServer) {
                args.add(String8("start-system-server"));
            }
    
            char prop[PROP_VALUE_MAX];
            if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
                LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                    ABI_LIST_PROPERTY);
                return 11;
            }
    
            String8 abiFlag("--abi-list=");
            abiFlag.append(prop);
            args.add(abiFlag);
    
            // In zygote mode, pass all remaining arguments to the zygote
            // main() method.
            for (; i < argc; ++i) {
                args.add(String8(argv[i]));
            }
        }
    
        if (!niceName.isEmpty()) {
            runtime.setArgv0(niceName.string());
            set_process_name(niceName.string());
        }
    
        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.");
            return 10;
        }
    }
    

    注释1处,通过 runtime 的start 函数 启动Zygote。init 进程 就分析到这里。

    相关文章

      网友评论

          本文标题:Android系统源码解析-init进程启动过程

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