本文介绍: Android和Android系统服务严格来说,Android系统实际是运行在Linux内核之上的一系列服务进程”,而这些服务进程的“老祖宗”就是init进程initAndroidPID1Androidinit.rcAndroidinit.rcinitmkdirmountinit.rcZygoteinitLinuxinit.rcinitAndroid 10对init进程的入口函数进行了调整,不再是之前的的main函数,**而是换成了的main

Android 设备启动必须经历三个阶段:Boot Loader、Linux Kernel 和 Android 系统服务。严格来说,Android 系统实际是运行在 Linux 内核之上的一系列“服务进程”,而这些服务进程的“老祖宗”就是 init 进程

img

按下启动电源时,系统启动后会加载引导程序引导程序启动 Linux 内核,在 Linux 内核加载完成后,第一件事情就是启动 init 进程。

Boot Loader 是在操作系统内核运行之前的一段小程序通过这段小程序可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适的状态以便为最终调用操作系统内核准备正确的环境。

init 进程是 Android 系统中用户空间第一个进程,PID(进程号)为 1,是 Android 系统启动流程一个关键的步骤,作为第一个进程,被赋予了很多极其重要的工作职责比如创建 Zygote 进程和属性服务等。

通过解析 init.rc 文件构建出系统的初始运行状态,即Android 系统服务大多是在 init.rc 脚本文件中有描述并按照一定的条件启动。

进程间通信

init 进程启动做了很多工作,总的来说主要做了以下三件事:

init 进程是由多个源文件共同组成的,这些文件位于源码目录 system/core/init 中。

在 Linux 内核加载完成后,首先会在系统文件中寻找 init.rc 文件(/system/core/rootdir/init.rc),并启动 init进程。

Android 10 对 init 进程的入口函数进行了调整,不再是之前的 system/core/init/init.cppmain 函数,而是换成了 system/core/init/main.cpp 的 main 函数,这样做的目的是把各个阶段工作分离开,使得代码逻辑更加简明,代码如下所示

// /system/core/init/main.cpp
// C++ 中主函数两个参数第一个参数 argc 表示参数个数第二个参数是参数列表,也就是具体的参数
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
    __hwasan_set_error_report_callback(AsanReportCallback);
#endif
    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);
  	/*
  	 * 1. strcmp 是 String一个函数比较字符串,相等返回 0
  	 * 2. C++ 中 0 也可以表示 false
  	 * 3. basename 是 C 库中一个函数,得到特定的路径中的最后一个 ‘/’ 后面的内容
  	 *    比如 /sdcard/miui_recovery/backup,得到的结果backup
  	 */
  	// 当 basename(argv[0]) 的内容为 ueventd 时 strcmp值为 0,!0 就是 1,执行 ueventd_main 函数
    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 = GetBuiltinFunctionMap();

            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
}

第一阶段:创建和挂载启动所需要文件目录

注释 1 处的代码是 init 进程的第一阶段,在 /system/core/init/first_stage_init.cpp 文件实现,代码如下所示

// /system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
      	// 初始化重启系统的处理信号内部通过 sigaction 注册新号,当监听到该信号重启系统
        InstallRebootSignalHandlers(); 
    }
 
    boot_clock::time_point start_time = boot_clock::now(); // 用于记录启动时间

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

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    
    /*---------- 创建和挂载启动所需的文件目录 ----------*/
  	// mount 是用来挂载文件系统的,mount 属于 Linux 系统调用
    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 新增,用于修改文件/目录读写权限
    gid_t groups[] = {AID_READPROC}; // 8.0 新增,增加了个用户组
  	// 用来list 数组中所标明的组加入到目前进程的组设置中
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载的 sysfs 文件系统
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); // 8.0 新增

    // mknod 用于创建 Linux 中的设备文件。这里提前创建了 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); 
    ...
}

由上面代码可知, init 进程的第一个阶段主要是用来创建和挂载启动所需的文件目录,其中挂载了 tmpfs、devptsproc、sysfs 和 selinuxfs 共 5 种文件系统,这些都是系统运行时目录,也就是说,只有在系统运行时才会存在,系统停止时会消失

四类文件系统:

第二阶段:初始化并启动属性服务

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

   ...
}

注释 1 处调用 PropertyInit() 函数来对属性进行初始化,并在注释 3 处调用 StartPropertyService 函数启动属性服务。

注释 2 处调用 InstallSignalFdHandler 函数用于设置子进程信号处理函数,主要是用来防止 init 进程的子进程成为僵尸进程。为了防止僵尸进程的出现,系统会在子进程暂停或者终止时候发出 SIGCHLD 信号,而 InstallSignalFdHandler 函数是用来接收 SIGCHLD 信号的(其内部只处理进程终止的 SIGCHLD 信号)。

假设 init 进程的子进程 Zygote 终止了,InstallSignalFdHandler 函数内部经过层层的函数调用和处理,最终会找到 Zygote 进程的信息,再重启 Zygote 服务的启动脚本

僵尸进程

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

孤儿进程

fork 函数调用之后,父子进程将交替执行执行顺序不定。如果父进程先退出,子进程还没有退出,那么子进程的的父进程将变成 init 进程(托孤个了 init 进程,这是因为任何一个进程都必须是父进程)。

属性服务

Window 平台上有一个注册表管理器注册表内容采取键值对的形式来记录用户软件的一些使用信息。即使系统或者软件重启,其还是能够根据之前注册表中的记录,进行相应的初始工作。Android 也提供了类似的机制,叫做属性服务。

init 进程启动时会启动属性服务,并为其分配内存用来存储这些属性,如果需要这些属性直接读取就可以了, 在 init.cpp 的 main 函数中与属性服务相关的代码有以下两行:

// /system/core/init/init.cpp
PropertyInit();
StartPropertyService(&property_fd);

这两行代码用来初始化属性服务配置并启动属性服务。

1 属性服务初始化与启动

PropertyInit() 函数的具体实现如下所示

// /system/core/init/property_service.cpp
void PropertyInit() {
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

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

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

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

    PropertyLoadBootDefaults();
}

注释 1 处的 __system_property_area_init() 函数用来初始化属性内存区域接下来看 StartPropertyService 函数的代码:

// /system/core/init/property_service.cpp
void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0];
    init_socket = sockets[1];
    StartSendingMessages();

    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | 
                                   SOCK_NONBLOCK, false, 0666, 0, 0, {}); // 1
        result.ok()) {
        property_set_fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(property_set_fd, 8); // 2

    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);
}

static void PropertyServiceThread() {
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        LOG(FATAL) << result.error();
    }

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

    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    while (true) {
        auto pending_functions = epoll.Wait(std::nullopt);
        if (!pending_functions.ok()) {
            LOG(ERROR) << pending_functions.error();
        } else {
            for (const auto& function : *pending_functions) {
                (*function)();
            }
        }
    }
}

注释 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 进程将调用 RegisterHandler 进行处理。

在 Linux 新的内核中,epoll 用来替换 select,epoll 是 Linux 内核为处理大批量文件描述符而做了改进的 poll,是 Linux 下多路复用 I/O 接口 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;

            const auto& cr = socket.cred();
            std::string error;
            uint32_t result =
                HandlePropertySet(prop_name, prop_value, socket.source_context(), 
                                  cr, &error); // 1
            ...

            break;
        }
        ...
    }

注释 1 处对 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
}

系统属性分为两种类型,一种是普通属性,另一种是控制属性,控制属性用来执行一些命令比如开机动画就使用了这种属性。因此,HandlePropertySet 分为两个处理分支,一部分处理控制属性,另一部分用于处理普通属性,这里分析普通属性。如果注释 1 处的属性名称以 ctl. 开头,说明是控制属性,如果客户端权限满足,则会调用 HandleControlMessage 函数来修改控制属性。如果是普通属性,则会在客户端权限妈祖的条件下调用注释 3 处的 PropertySet 来对普通属性进行修改如下所示

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

PropertySet 函数主要对普通属性进行修改,首先要判断该属性是否合法,如果合法就在注释 1 处从属性存储空间查找该属性,如果属性存在,就更新属性值,否则就添加该属性。另外,还对名称以 ro. persist. 开头的属性进行了相应的处理。

第三阶段:解析 init.rc 配置文件

// /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.rc 是一个非常重要的配置文件,它是由 Android 初始语言(Android Init Language) 编写脚本,这种语言主要包含 5 种类型语句:Action(行为)、Command(命令)、Service(服务)、Option(选项) 和 Import(引入)。

init.rc 配置init.rc 文件大致分为两大部分,一部分是以 on 关键字开头的“动作列表”(action list),另一部分是以 service 关键字开头的“服务列表”(service list)。

动作列表用于创建所需目录以及为某些特定文件指定权限,服务列表用来记录 init 进程需要启动的一些子进程。 如下所示

// /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 init 和 on boot 是 Action 类型语句,它的格式如下所示:

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

对于如何创建 Zygoet ,主要查看 Service 类型语句

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

service 类型语句格式

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

需要注意的是,在 Android 8.0 中对 init.rc 文件进行了拆分每个服务对应一个 .rc 文件。Zygote 启动脚本在 init.zygoteXX.rc 中定义,以 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 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

根据 Service 类型语句格式分析上面代码的意思。Service 用于通知 init 进程创建名为 Zygote 的进程,这个进程的执行路径为 /system/bin/app_process64,后面的代码是要传给 app_process64 的参数。class main 指的是 Zygote 的 classname 为 main。

解析 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,ParseSection 函数主要用来搭建 Service 的架子;另一个是 ParseLineSection,永远解析子项。代码如下所示:

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

注释 1 处,根据参数构造出一个 Service 对象。在解析完数据后会调用 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();
}

EndSection 函数中回调用 ServiceManager.AddService 函数,以下是 AddService 函数:

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

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

第四阶段:启动 Zygote 进程

了解了 Service 解析过程后,接着就是 init 进程如何启动 Service,这里主要讲解启动 Zygote 这个 Service。在 Zygote 的启动脚本中,可以知道 Zygote 的 classname 为 main。在 init.rc 中有如下配置代码:

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

其中,class_start 是一个 COMMAND,对应的函数是 do_class_start。注释 1 处启动那些 classname 为 main 的 Service,Zygote 的 classname 就是 main,因此,class_start main 是用来启动 Zygote 的,do_class_start 函数在 builtins.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();
}

在注释 1 处会遍历 Service 链表找到 classname 为 main 的 Zygote ,并且执行 StartIfNotDisabled 函数,如下所示:

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

在注释 1 处,如果 Service 没有找到其对应的 .rc 文件中设置的 disable 选项,则会调用 Start() 函数启动该 Service,Zygote 对应的 init.zygote64.rc 中并没有设置 disabled 选项,因此接着查看 Start() 函数:

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

首先判断 Service 是否已经运行,如果运行则不再启动,如果程序走到注释 1 处,说明子进程还没有被启动,就调用 fork() 函数创建子进程,并返回 pid 值,注释 2 处,如果 pid == 0,则说明当前代码的逻辑在子线程中运行。在注释3 处调用 execv 函数,Service 子进程就会被启动,并且进入该 Service 的 main 函数中

如果该 Service 是 Zygote,Zygote 的执行路径为 system/bin/app_process64,对应的文件为 app_main.cpp,这样就会进入 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 就启动了。

参考

init.rc详解
SystemServer服务和ServiceManager服务分析
【 Android 10 系统启动 】系列 – init(天字一号进程)

原文地址:https://blog.csdn.net/xingyu19911016/article/details/127451545

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_37706.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注