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结构说明

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容