Android system startup (1) - init process startup process

AndroidThe startup of the device must go through three phases: Boot Loader, Linux Kerneland Androidsystem services. Strictly speaking, Androidthe system is actually a series of "service processes" running on Linuxthe kernel, and the "ancestor" of these service processes is initthe process.

Android system startup process

Boot LoaderIt is a small program before the operating system kernel runs. Through this small program, the hardware device can be initialized, and the mapping map of the memory space can be established, so as to bring the software and hardware environment of the system to a suitable state, so as to prepare the correct environment for the final call of the operating system kernel.

initThe process is Androidthe first process in the user space in the system, PID(process number) is 1, it is Androida key step in the system startup process, as the first process, it is given a lot of extremely important work responsibilities, it is init.rcconstructed by parsing files The initial running state of the system, that is, Androidmost of the system services init.rcare described in the script file and started according to certain conditions.

init process

initProcess startup has done a lot of work, in general, it mainly does the following three things:

  • Create ( mkdir) and mount ( mount) the file directories required for startup;
  • Initialize and start property service( property service);
  • Parse init.rcthe configuration file and start Zygotethe process;

initA process is composed of multiple source files, which are located in the source directory system/core/init.

After the kernel is loaded, it will first look for the file ( ) Linuxin the system files , and start the process. init.rc/system/core/rootdir/init.rcinitThe entry function of the process has been adjusted. It is no longer the function of the previous , but has been replaced with the function of . Android 10The purpose of this is to separate the work of each stage and make the code logic more concise. **The code is as follows Shown:init/system/core/init/init.cppmainsystem/core/init/main.cppmain

// /system/core/init/main.cpp
int main(int argc, char** argv) {
    #if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
    #endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv); // 1
}

The first stage: create and mount the file directory required for startup

1The code at the comment is initthe first stage of the process, which is implemented first_state_init.cppin the file ( /system/core/init/first_stage_init.cpp), and the code looks like this:

// /system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers(); 
    }
 
    boot_clock::time_point start_time = boot_clock::now(); // 用于记录启动时间

    umask(0); // Clear the umask. 清理umask

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    
    /*---------- 创建和挂载启动所需的文件目录 ----------*/
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); // 挂载 tmpfs 文件
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); // 挂载 devpts 文件系统

#define MAKE_STR(x) __STRING(x)
  
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); // 挂载 proc 文件系统
#undef MAKE_STR
    
    CHECKCALL(chmod("/proc/cmdline", 0440)); // 8.0 新增,收紧了 cmdline 目录的权限
    gid_t groups[] = {AID_READPROC}; // 8.0 新增,增加了个用户组
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载的 sysfs 文件系统
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); // 8.0 新增

    // 提前创建了 kmsg 设备节点文件,用于输出 log 信息
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));

    CHECKCALL(mkdir("/mnt/vendor", 0755)); // 创建可供读写的 vendor 目录
    CHECKCALL(mkdir("/mnt/product", 0755)); // 创建可供读写的 product 目录

    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL
    SetStdioToDevNull(argv);
	// 初始化 Kernel Log 系统,这样就可以从外界获取 Kernel 日志
    InitKernelLogging(argv); 
    ...
}

As can be seen from the above code, initthe first stage of the process is mainly used to create and mount the file directories required for startup , in which tmpfs, devpts, proc, sysfsand selinuxfsare mounted 5file systems, which are all system runtime directories, that is to say , will only exist when the system is running, and will disappear when the system is stopped.

Four types of file systems:

  • tmpfs: A virtual memory file system that stores all files in the virtual memory. If tmpfsthe file system is unmounted, all the contents under it will no longer exist. tmpfsBoth can be used RAM, or swap partition can be used, and the size will be changed according to actual needs. tmpfsThe speed of is amazing, after all, it resides in RAM, even with the swap partition, the performance is still very good. Since tmpfsthe resides in RAM, its contents are not persistent. After the power is turned off, tmpfsthe content of the content disappears, which is also tmpfsthe root cause of being called ;
  • devpts: Provides a standard interface for the pseudo-terminal, and its standard mount point is /dev/pts. As long as ptythe main compound device of is opened, a new device file /dev/ptmxwill be dynamically created /dev/ptsunder ;pty
  • proc: A very important virtual file system, which can be regarded as the interface of the internal data structure of the kernel, through which we can obtain system information, and can also modify specific kernel parameters at runtime;
  • sysfs: procSimilar to the file system, it is also a virtual file system that does not occupy any disk space. It is usually mounted /sysunder the directory. sysfsThe file system is Linux 2.6introduced by the kernel, which organizes the devices and buses connected to the system into a hierarchical file so that they can be accessed in user space;

Phase 2: Initialize and start the property service

// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
    
    
    ...
    // 1 初始化属性服务
    property_init();
    ...
    // 创建 epoll 句柄
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
    
    
        PLOG(FATAL) << result.error();
    }
    // 2 创建 Handler 处理子进程的终止信号,如果子进程(Zygote)异常退出,init进程会调用该函数中设定的信号处理函数来进行处理 
    InstallSignalFdHandler(&epoll);

    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    // 3 启动属性服务
    StartPropertyService(&epoll);
	...
}

1Call the function at the comment property_init()to initialize the property, and 3call StartPropertyServicethe function at the comment to start the property service.

2Calling the function at the comment InstallSignalFdHandleris used to set the subprocess signal processing function, mainly to prevent initthe subprocess of the process from becoming a zombie process. In order to prevent the emergence of zombie processes, the system will send a signal when the child process is suspended or terminated SIGCHLD, and InstallSignalFdHandlerthe function is used to receive SIGCHLDthe signal (it only processes SIGCHLDthe signal of process termination internally).

zombie process

Zombie process and harm: In UNIX/Linux, the parent process uses forkthe method to create a child process. After the child process terminates, if the parent process cannot be notified, although the child process has already exited, a certain amount of time is reserved for it in the system process table. Information (such as process number, exit status, running time, etc.), this process is called a zombie process. The system process table is a limited resource. If the system process table is exhausted by zombie processes, the system may not be able to create new processes.

attribute service

WindowThere is a registry manager on the platform, and the content of the registry takes the form of key-value pairs to record some usage information of users and software. Even if the system or software is restarted, it can still perform corresponding initialization work according to the previous records in the registry. AndroidA similar mechanism is also provided, called property services.

initWhen the process starts, the attribute service will be started, and memory will be allocated for it to store these attributes. If you need to read these attributes directly, the code related to the attribute service in the function of will have the following two lines init.cpp:main

// /system/core/init/init.cpp
property_init();
StartPropertyService(&epoll);

These two lines of code are used to initialize the property service configuration and start the property service.

1 Property service initialization and startup

property_initThe specific implementation of the function is as follows:

// /system/core/init/property_service.cpp
void property_init() {
    
    
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
    
    
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
    
    
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}

1The function at comment __system_property_area_init()is used to initialize the attribute memory area. Next look at StartPropertyServicethe code of the function:

// /system/core/init/property_service.cpp
void StartPropertyService(Epoll* epoll) {
    
    
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    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); // 1
    if (property_set_fd == -1) {
    
    
        PLOG(FATAL) << "start_property_service socket creation failed";
    }

    listen(property_set_fd, 8); // 2

    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
    
     // 3
        PLOG(FATAL) << result.error();
    }
}

1Create non-blocking at comment Socket. 2Call listenthe function at the comment property_set_fdto listen to , so that the created Socketbecomes server, that is, the attribute service; listenthe second parameter of the function is set to 8, which means that the attribute service can 8provide services for at most users who try to set attributes at the same time. 3The code at the comment property_set_fdputs in epollto epollmonitor property_set_fd: property_set_fdwhen data arrives in , initthe process will call RegisterHandlerfor processing.

In Linuxthe new kernel, it is epollused to replace the kernel select, which has been improved to handle a large number of file descriptors . It is an enhanced version of the multiplexing interface , which can significantly improve the performance of programs with only a small number of active connections in a large number of concurrent connections. The system utilization under the condition of . The internal data type used to save time is a red-black tree, which has a fast search speed. The array used to store information has a very slow search speed. Only when waiting for a small number of file descriptors, the efficiency of and will be similar.epollLinuxpollLinuxI/Oselect/pollCPUepollselectepollselect

2 The service processes client requests

From the above, we can know that when the attribute service receives the request from the client, it will call handle_property_set_fdthe function to process it:

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

            const auto& cr = socket.cred();
            std::string error;
            uint32_t result =
                HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error); // 1
            if (result != PROP_SUCCESS) {
    
    
                LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
                    << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                    << error;
            }

            break;
        }
        ...
    }

The function is further encapsulated 1at the comment , as shown below:handle_property_set_fd

// /system/core/init/property_service.cpp
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
    
    
    ...
	// 如果属性名称以 `ctl.` 开头,说明是控制属性
    if (StartsWith(name, "ctl.")) {
    
     // 1
        // 设置控制属性
        HandleControlMessage(name.c_str() + 4, value, cr.pid); // 2
        return PROP_SUCCESS;
    }

    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") {
    
    
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
    
    
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
            << process_log_string;
    }

    if (name == "selinux.restorecon_recursive") {
    
    
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }

    return PropertySet(name, value, error); // 3
}

There are two types of system attributes, one is common attributes, and the other is control attributes, which are used to execute some commands, such as the startup animation. Therefore, it HandlePropertySetis divided into two processing points, one part deals with control attributes, and the other part is used to deal with common attributes. Only common attributes are analyzed here. If 1the attribute name at the comment ctl.starts with , it means it is a control attribute. If the client permission is satisfied, HandleControlMessagethe function will be called to modify the control attribute. If it is an ordinary attribute, under the condition of the client authority Mazu, the comment at the comment will be called 3to PropertySetmodify the ordinary attribute, as shown below:

// /system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    
    
    size_t valuelen = value.size();
	// 判断是否合法
    if (!IsLegalPropertyName(name)) {
    
    
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    ...
	// 从属性存储空间查找该属性
    prop_info* pi = (prop_info*) __system_property_find(name.c_str()); // 1
    // 如果属性存在
    if (pi != nullptr) {
    
    
        // ro.* properties are actually "write-once". 如果属性名称以"ro."开头,则表示只读,不能修改,直接返回
        if (StartsWith(name, "ro.")) {
    
    
            *error = "Read-only property was 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) {
    
    
            *error = "__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 && StartsWith(name, "persist.")) {
    
    
        WritePersistentProperty(name, value);
    }
    property_changed(name, value);
    return PROP_SUCCESS;
}

PropertySetThe function mainly modifies common attributes. Firstly, it is necessary to judge whether the attribute is legal. If it is legal, 1search the attribute from the attribute storage space at the annotation. If the attribute exists, update the attribute value; otherwise, add the attribute. Also, ro. persist.properties whose names begin with are handled accordingly.

The third stage: parsing init.rcthe configuration file

// /system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    
    
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
    
    
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
    
    
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
    
    
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
    
    
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
    
    
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
    
    
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
    
    
        parser.ParseConfig(bootscript);
    }
}  

init.rcis a very important configuration file, it is a script written by Androidthe initialization language ( ), this language mainly contains three types of statements: , , , and .Android Init Language5Action(行为)Command(命令)Service(服务)Option(选项)Import(引入)

Configuration of init.rc

init.rcThe file is roughly divided into two parts, one part is the "action list" ( ) onstarting with the keyword , and the other part is the "service list" ( ) starting with the keyword .action listserviceservice list

The action list is used to create the required directory and specify permissions for some specific files, and the service list is used to record initsome sub-processes that the process needs to start. As follows:

// /system/core/rootdir/init.rc
on init
    sysclktz 0
    copy /proc/cmdline /dev/urandom
    copy /system/etc/prop.default /dev/urandom
    ...
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    ...

on initand on bootis Actiona statement of type, its format is as follows:

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

For how to create Zygoet, mainly look at Servicethe type statement , its format is as follows:

service ueventd /system/bin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0
    shutdown critical

ServiceType statement format:

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

It should be noted that the file is split in , and each service corresponds to a Android 8.0file . Startup scripts are defined in , take for example :init.rc.rcZygoteinit.zygoteXX.rcinit.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 reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary 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

Analyze the meaning of the above code according to Servicethe format of the type statement. ServiceIt is used to notify initthe process to create Zygotea process named , the execution path of this process is /system/bin/app_process64, and the following code is app_process64the parameter to be passed to . class mainRefers to Zygotethe classnamefor main.

Serviceparse type statement

init.rcBoth Actionthe type statement and Servicethe type statement in have corresponding classes for parsing, Actionthe type statement uses ActionParserfor parsing, and Servicethe type statement uses ServiceParserfor parsing. Because the main analysis is here Zygote, it is only introduced ServiceParser.

ServiceParserThe implementation code is system/core/init/service.cppin . Next, check ServiceParserhow to parse the above-mentioned Servicetype of statement, two functions will be used: one is that ParseSectionit will parse the file Serviceof .rcthe , for example, as mentioned above init.zygote64.rc, ParseSectionthe function is mainly used to build Servicethe shelf of the; the other is ParseLineSection, resolves subkeys forever. The code looks like this:

// system/core/init/service.cpp
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    
    
    if (args.size() < 3) {
    
     // 判断Service是否有name与可执行程序
        return Error() << "services must have a name and a program";
    }

    const std::string& name = args[1];
    if (!IsValidName(name)) {
    
     // 检查Service的name是否有效
        return Error() << "invalid service name '" << name << "'";
    }

    filename_ = filename;

    Subcontext* restart_action_subcontext = nullptr;
    if (subcontexts_) {
    
    
        for (auto& subcontext : *subcontexts_) {
    
    
            if (StartsWith(filename, subcontext.path_prefix())) {
    
    
                restart_action_subcontext = &subcontext;
                break;
            }
        }
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());

    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
    
    
        if (str_args[0] == "/sbin/watchdogd") {
    
    
            str_args[0] = "/system/bin/watchdogd";
        }
    }

    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args); // 1
    return Success();
}

Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    
    
    return service_ ? service_->ParseLine(std::move(args)) : Success();
}

At the comment 1, an object is constructed according to the parameters Service. The function is called after parsing the data EndSection:

// system/core/init/service.cpp
Result<Success> ServiceParser::EndSection() {
    
    
    if (service_) {
    
    
        Service* old_service = service_list_->FindService(service_->name());
        if (old_service) {
    
    
            if (!service_->is_override()) {
    
    
                return Error() << "ignored duplicate definition of service '" << service_->name()
                    << "'";
            }

            if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
    
    
                return Error() << "cannot update a non-updatable service '" << service_->name()
                    << "' with a config in APEX";
            }

            service_list_->RemoveService(*old_service);
            old_service = nullptr;
        }

        service_list_->AddService(std::move(service_));
    }

    return Success();
}

EndSectionThe function calls back ServiceManager.AddServicethe function, the following is AddServicethe function:

// system/core/init/service.cpp
void ServiceList::AddService(std::unique_ptr<Service> service) {
    
    
    services_.emplace_back(std::move(service)); // 1
}

1The code at the comment Serviceadds the object to Servicethe linked list. ServiceGenerally speaking, the parsing process is to create Servicethe object according to the parameters, then fill Servicethe object according to the content of the option field, and finally Serviceadd the object to the linked list vectorof type Service.

Phase Four: Initiating Zygotethe Process

After understanding Servicethe parsing process, the next step is inithow to start the process Service. Here we mainly explain how to start Zygotethe process Service. In Zygotethe startup script of , you can know the behavior Zygoteof . In has the following configuration code:classnamemaininit.rc

// /system/core/rootdir/init.rc
on nonencrypted
    class_start main // 1
    class_start late_start

Among them, class_startis one COMMAND, and the corresponding function is do_class_start. The comment 1that starts those for classnameis , therefore , is used to start the function defined in as follows:mainServiceZygoteclassnamemainclass_start mainZygotedo_class_startbuiltins.cpp

// /system/core/init/builtins.cpp
static Result<Success> do_class_start(const BuiltinArguments& args) {
    
    
    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
        return Success();
    // Starting a class does not start services which are explicitly disabled.
    // They must  be started individually.
    for (const auto& service : ServiceList::GetInstance()) {
    
     // 1
        if (service->classnames().count(args[1])) {
    
    
            if (auto result = service->StartIfNotDisabled(); !result) {
    
    
                LOG(ERROR) << "Could not start service '" << service->name()
                    << "' as part of class '" << args[1] << "': " << result.error();
            }
        }
    }
    return Success();
}

At the comment 1, it will traverse Servicethe linked list, find classnamethe one that mainis Zygote, and execute StartIfNotDisabledthe function, as shown below:

// /system/core/init/service.cpp
Result<Success> Service::StartIfNotDisabled() {
    
    
    if (!(flags_ & SVC_DISABLED)) {
    
     // 1
        return Start();
    } else {
    
    
        flags_ |= SVC_DISABLED_START;
    }
    return Success();
}

At the comment 1, if the option set in Servicethe corresponding file is not found , the function will be called to start it , and the option is not set in the corresponding , so then check the function:.rcdisableStart()ServiceZygoteinit.zygote64.rcdisabledStart()

// /system/core/init/service.cpp
Result<Success> Service::Start() {
    
    
    ...
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    // 如果Service已经运行,则不启动
    if (flags_ & SVC_RUNNING) {
    
     
        if ((flags_ & SVC_ONESHOT) && disabled) {
    
    
            flags_ |= SVC_RESTART;
        }
        // It is not an error to try to start a service that is already running.
        return Success();
    }
    ...
    // 判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
    
    
        flags_ |= SVC_DISABLED;
        return ErrnoError() << "Cannot find '" << args_[0] << "'";
    }
    ...
    // 如果子进程没有启动,则调用fork函数创建子进程
    pid_t pid = -1; // 1
    if (namespace_flags_) {
    
    
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
    
    
        pid = fork();
    }

    // 当前代码逻辑在子进程中运行
    if (pid == 0) {
    
     // 2
        umask(077);
		...
        if (!ExpandArgsAndExecv(args_, sigstop_)) {
    
    
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    }
}
    
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
    
    
    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);

    if (sigstop) {
    
    
        kill(getpid(), SIGSTOP);
    }

    // 调用execv函数,启动service子进程
    return execv(c_strings[0], c_strings.data()) == 0; // 3
}

First judge Servicewhether has been running, if it is running, it will not start again, if the program goes to the comment 1, it means that the sub-process has not been started, then call fork()the function to create a sub-process, and return pidthe value, 2if in the comment pid == 0, it indicates the logic of the current code run in sub-threads. 3Call the function at the comment execv, Servicethe child process will be started and enter the function Serviceof the main.

If the Serviceis Zygote, Zygotethe execution path is system/bin/app_process64, and the corresponding file is app_main.cpp, so it will enter the function app_main.cppof main, that is, in the function Zygoteof main, the code is as follows:

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

1The code at the comment is to call runtime.startthe function to start Zygote, so far, Zygoteit has started.

reference

init.rc Detailed
Analysis of SystemServer Service and ServiceManager Service
[Android 10 System Startup] Series – init (Tianzi No. 1 process)

Guess you like

Origin blog.csdn.net/xingyu19911016/article/details/127451545