Android系统从init进程到Launcher(一)

前言

Android系统中最先被我们感知的就是Launcher界面,对于基于Android系统的智能设备来说,对Launcher进行定制是非常重要的一个环节,那么了解Launcher的加载和启动就显得尤为重要,因此接下来会用几章的篇幅进行分析阐述其中的原理。

Launcher启动流程图

对上述流程图略作说明,当按下启动电源时,系统启动后会加载引导程序,引导程序又会启动 Linux 内核,在加载 Linux 内核时,它会在系统文件中寻找 init.rc 文件,并启动 init 进程,之后会按着上图中绿色部分依次启动直至最后启动 Launcher , 至此Android桌面展现。实际上Android系统的启动流程要比上图复杂的多,为了便于理解做了简化,而接下来的几篇会按着 init、Zygote、SystemService、ActivityManagerService、Launcher 这五个方面分别论述。

各进程间关系

由图中可以看出,他们之间的通讯过程大致是:

  1. Launcher通过AIDL(Binder的一种)通知SystemService
  2. SystemService通过Socket通知Zygote
  3. Zygote 收到SystemService请求,fork自身生成应用程序
  4. 应用程序进程与SystemService通过Binder与AMS,WMS进行交互

Init 进程任务

  1. 创建和挂载启动所需要的文件目录
  2. 初始化和启动属性服务
  3. 解析 init.rc 配置文件并启动 Zygote 进程

Init 源码分析

为避免陷入到庞杂的源码中,本小节会尽量的抽取 init 进程中的主要脉络作为说明.
init 进程是 Android 启动的第一个进程,进程号为 1 ,其源码主要存在于 "/system/core/init" 目录下。

(1) Init 进程入口

init 进程的入口函数为 main ,代码如下所示:

int main(int argc, char** argv) {
    
    ...

    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask.
        umask(0);

        clearenv();
        
        // 创建和挂载启动所需的文件目录 注释 ①   
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        chmod("/proc/cmdline", 0440);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
        mkdir("/mnt/vendor", 0755);
        ...
        
        // 初始化 Kernel 的 Log,这样就可以从外界获取 Kernel 的日志  注释②
        InitKernelLogging(argv);
        
    }
    ...
    // 初始化属性服务配置 注释 ③
    property_init();

    ...
    // 用于设置紫禁城信号处理函数,如果子进程(Zygote进程)异常退出,init 进程会调用该函数中
    //设定的信号处理函数来进行处理  注释 ④
    sigchld_handler_init();

  
    property_load_boot_defaults();
    export_oem_lock_status();
   // 启动属性服务 注释 ⑤
    start_property_service();
    set_usb_controller();
    ...
    // 加载和解析 init.rc 文件 注释⑥
    LoadBootScripts(am, sm);
  
    return 0;
}

(2) 属性服务

Windows 平台采用注册表管理器来保存用户、软件的一些使用信息。这样即使系统或者软件重启,就可以根据注册表中的记录,进行相应的初始化工作。Android 提供类似的机制,叫做 属性服务
在上述 main 函数中与属性服务相关的代码有两行:

property_init();
start_property_service();

先来看看两个函数的具体实现:"/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";
    }
}

__system_property_area_init 函数用来初始化属性内存区域,接下来看看 start_property_service 函数的具体实现:

void start_property_service() {
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");
    
    // 创建非阻塞的 Socket   注释 ①
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    }
    // 调用 listen 函数监听 property_set_fd 注释 ②
    listen(property_set_fd, 8);
    // epoll 替换 select 处理大批量的文件描述符,handle_property_set_fd 用来处理客户端的请求
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

可以看出在 注释 ② 处调用 listen 函数对 property_set_fd 进行监听,这样创建的 Socket 就成为 server,也就是 属性服务listen 函数的第二个参数设置为 8 ,意味着属性服务最多可以同时为8个试图设置属性的用户提供服务;

(3) 加载和解析 init.rc 文件

init 进程通过解析 init.rc 文件去启动 Zygote 进程,这里通过 LoadBootScripts() 函数实现:
/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("/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);
    }
}

如果没有特殊配置ro.boot.init_rc,则解析./init.rc,把 /system/etc/init、/product/etc/init、/product_services/etc/init、/odm/etc/init、/vendor/etc/init 这几个路径加入init.rc之后解析的路径,在init.rc解析完成后,解析这些目录里的rc文件。这里需要说明一下,在 Android 7.0 之后,init.rc 进行了拆分,每个服务都有自己的 rc 文件,它们基本都被加载到这几个目录中,待解析之后,执行相关的动作。关于 init.rc 文件的组成结构可以参见 init.rc结构说明

你可能感兴趣的:(Android系统从init进程到Launcher(一))