Android 8.0系统启动流程_init(一)

本系列主要介绍Android8.0系统启动过程中涉及到的init、ZygoteSystemServerLauncher

  1. init启动前准备
  2. init函数分析
  3. init函数解析
  4. init启动Zygote

一、init启动前准备

init进程是Android系统的第一个进程,其进程号为1,该进程启动后,主要处理一些重要的初始化工作,比如创建Zygote和各种属性服务。在电源按键,按下后,经历如下几个步骤后,便开始引入init进程:

1. 启动电源及系统:电源按下后,引导芯片开始将固化在ROM中的BootLoader加载至RAM中,并执行该程序;
2. 引导程序BootLoader:引导程序BootLoader是在Android操作系统开始运行前,执行的一个小程序,其主要作用是把系统OS拉起来并运行;
3. Linux内核启动:内核启动时,设置缓存、被保护存储器、计划列表和加载驱动等,完成设置后,开始加载init.rc文件;
4. init进程启动:开始初始化和启动属性服务,创建Zygote进程。

二、 init函数分析

在Linux内核加载完成后,开始查找init.rc文件,并启动init进程,其主要代码如下:

system/core/init/init.cpp

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

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

        // 清理 umask.
        umask(0);

        // 创建和挂载所需的文件
        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));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

        // 初始化Kernel日志
        InitKernelLogging(argv);
        ...
    }

   ...
    //属性服务初始化
    property_init();

    ...
    
    //创建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    
   ...
   
    //设置子进程的处理函数
    signal_handler_init();

    //导入默认的环境变量
    property_load_boot_defaults();
    export_oem_lock_status();
    //启动属性服务
    start_property_service();
    set_usb_controller();

    ...
    
    if (bootscript.empty()) {
    //解析init.rc文件
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }

    ...

    return 0;
}

该流程分析如下:

1. 挂载文件:通过设置umask值,屏蔽一些权限后,开始挂载和创建所需的文件;
2. 属性服务:通过property_init()属性服务初始化,调用start_property_service()启动属性服务;
3. 子进程的处理函数: signal_handler_init()主要作用是防止出现僵尸进程,子进程在暂停和终止后,会发出SIGCHLD的信号,signal_handler_init()接收到该信号后,会对该进程进行回收处理,防止占用系统进程资源;
4. 解析init.rc配置:在8.0中对init.rc文件进行了拆分,可查看system\core\rootdir目录,包括:
init.zygote32.rc:Zygote对应的执行程序是app_process(纯32位模式);
init.zygote64.rc:Zygote对应的执行程序是app_process64(纯64位模式);
init.zygote32_64.rc:启动两个Zygote进程( zygote 和 zygote_secondary),对应的执行程序是app_process32(主模式)和app_process64;
init.zygote64_32.rc:启动两个Zygote进程( zygote 和 zygote_secondary),对应的执行程序是app_process64(主模式)和app_process32。

三、 init函数解析

\system\core\rootdir\init.rc

init.rc配置文件,是由Android初始化语言(Android Init Language)编写的脚本,主要包括5种语言类型:Action、Command、Service、Option和Import。
同时,需要注意init.rc仅仅是配置文件,类似于xml,没有解析的顺序而言(不是自上而下运行),是通过程序调用,获取相应的配置信息,真正加载运行的程序是在上部分提到的init.cpp文件。

import:指令是用于类似于java语言,主要用于扩展配置,格式如下:

import <filename>一个init配置文件,扩展当前配置

filename一般是init.XXX.rc的形式,源码中例如:

import /init.environ.rc

Action:一系列命令的开始,其格式如下:

on <trgger> [&& <trigger>]*
   <command1>
   <command2>
   <command3>
   ...

每个Action都至少有一个触发器(trgger),该触发器决定何时执行该Action,即后面的command...指令,其要通过ActionParser解析,其实现代码在\system\core\init\action.cpp,其格式如下:

# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete
    rm /dev/.booting

Service:以service开头,由init进程启动的,一般运行于init的子进程中,在启动该service前需要判断对应的可执行文件是否存在,并通过fork方式生成子进程。其主要通过ServiceParser解析,其实现代码在\system\core\init\service.cpp,其格式如下:

service <name> <pathname> [ <argument> ]*
    <option>
    <option>
    ...

其中name:服务的名称,pathname:当前服务对应程序的位置,option:当前服务设置的选项,argument:可选参数。例如:

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

四 、init启动Zygote

\system\core\rootdir\init.rc
\system\core\init\init.cpp
\system\core\init\builtins.cpp
\ frameworks\base\cmds\app_process\app_main.cpp

在init.rc中,关于Zygote的启动在如下部分:

\system\core\rootdir\init.rc

on nonencrypted
    class_start main
    class_start late_start

其中 main指的就是Zygote的服务名称,其中class_start函数是指builtins.cpp中的函数:

\system\core\init\builtins.cpp

static int do_class_start(const std::vector<std::string>& args) {
    ServiceManager::GetInstance().
        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
    return 0;
}

其中ForEachServiceInClass函数会遍历Service链表,查找classname为main的Zygote,并执行service.cpp中的StartIfNotDisabled函数,如下所示:

\system\core\init\service.cpp

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

如果该service没有在其对应的rc文件中设置disable选项,则会执行Start()方法,启动该Service,在init.zygote64.rc中没有设置disable选项,因此可以正常启动该Service。

\system\core\init\builtins.cpp

bool Service::Start() {
    ..
    //是否正在运行
    if (flags_ & SVC_RUNNING) {
        return false;
    }

   ...
   //启动的路径是否存在
    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
        flags_ |= SVC_DISABLED;
        return false;
    }

    std::string scon;
    if (!seclabel_.empty()) {
        scon = seclabel_;
    } else {
        scon = ComputeContextFromExecutable(name_, args_[0]);
        if (scon == "") {
            return false;
        }
    }

   //如果该进程未启动,调用fork启动该进程
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) {
        umask(077);

      ...
    }

    if (pid < 0) {
        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
        pid_ = 0;
        return false;
    }

    ....
    //启动该进程
    SetProcessAttributes();

        if (!ExpandArgsAndExecve(args_)) {
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    NotifyStateChange("running");
    return true;
}


其主要流程如下:

  • 是否正在运行,如正在运行,直接返回;
  • 子进程是否启动,如未启动,调用fork并返回pid值;
  • 如果以上正常,则调用ExpandArgsAndExecve启动该进程,完成Zygote的启动。

启动Zygote进程真正执行的路径为frameworks\base\cmds\app_process\app_main.cpp下的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);//启动Zygote进程
    } 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.");
    }
...
 }

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

推荐阅读更多精彩内容