Android启动流程-init进程分析


基于Android 10 源码分析

之前在Android启动流程分析中已经大致分析了一下Android系统启动的过程,这里回顾一下:

  1. 上电,从Boot ROM中执行一段烧录好的代码加载bootloader
  2. bootloader执行,引导加载并启动linux kernel
  3. linux kernel启动,并创建第一个用户态的进程init
  4. init进程解析init.rc并启动各种服务,包括通过app_process程序创建并启动Zygote进程和system server进程,这个阶段已经启动了虚拟机,进入了Java世界
  5. system server进程会启动各种服务,包括AMS,WMS等
  6. 启动完成之后,发送广播通知

这里来分析一下init进程的一些主要逻辑:

强调一点,init就是一个普通的命令行程序,程序位置在/system/core/init

入口:system/core/init/main.cpp

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

    // system/bin/ueventd入口
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    // 带参启动init
    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);
        }
    }

    // 不带参,默认启动init入口
    return FirstStageMain(argc, argv);
}

main(int argc, char** argv) ,其中argc是命令行参数的个数,默认为1,argv是传入的参数,argv[0]是程序的全路径名,arg[1]才是实际意义上的第一个参数.

strcmp(s1,s2)则是用来比较字符串的,如果s1 == s2,则返回0

basename(path),返回的是文件名,比如 “/system/bin/ueventd”,返回uevened

ueventd其实就是init进程的一个软连接,在system/bin中也可以看到,通过ls -l可以看到:

lrwxr-xr-x  1 root   shell          4 2020-04-04 01:11 ueventd -> init

这个地方根据程序不同的执行方式,有三种入口:

  • 默认启动,执行FirstStageMain(argc, argv)
  • 启动ueventd程序,执行ueventd_main(argc, argv)
  • 带参启动init,执行SubcontextMain(argc, argv, &function_map);,SetupSelinux(argv),SecondStageMain(argc, argv)中的一个

目前很多文章都说watchdogd进程也是init进程的软连接,但是在新版本的Android中,比如Android 10,watchdogd是一个独立的进程了,并不是init进程.

先来看下FirstStageMain

第一阶段: FirstStageMain

源码在:system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));

    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));

    // root也只有只读权限
    CHECKCALL(chmod("/proc/cmdline", 0440));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    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)));
    // This is needed for log wrapper, which gets called before ueventd runs.
    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));
    CHECKCALL(mkdir("/mnt/product", 0755));

    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"));

    SetStdioToDevNull(argv);
    InitKernelLogging(argv);

    LOG(INFO) << "init first stage started!";

    // execv方式执行 /system/bin/init selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));

    return 1;
}

setenv():设置环境变量

mount():挂载文件系统:

// source是一个名字,target是挂载点,filesystemtype是文件系统类型
// 文件系统类型有"btrfs", "ext4", "jfs", "xfs","vfat", "fuse", "tmpfs", "cgroup", "proc", "mqueue", "nfs", //"cifs","iso9660"等
int mount(const char *source, const char *target,
                 const char *filesystemtype, unsigned long mountflags,
                 const void *data);

mknod():Linux中创建字符设备文件或者块设备文件

tmpfs:一个基于内存的文件系统,断电后数据丢失

ABCD
A- 0, 表示十进制
B-用户
C-组用户
D-其他用户

---  -> 0   (no excute , no write ,no read)
--x  -> 1   excute, (no write, no read)
-w-  -> 2   write 
-wx  -> 3   write, excute
r--  -> 4   read
r-x  -> 5   read, excute
rw-  -> 6   read, write , 
rwx  -> 7   read, write , excute

0755:用户具有读/写/执行权限,组用户和其它用户具有读写权限

可以看到这个阶段主要是挂载一些文件系统.并启动SELinux.

启动SELinux

这个过程中会执行:

    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

然后进入到init进程的第二个阶段

第二阶段:SecondStageMain

在第一阶段的源码中有一饿SecondStageMain的方法调用,这个时候会进入第二个执行阶段:

int SecondStageMain(int argc, char** argv) {

    LOG(INFO) << "init second stage started!";

      // 会执行 /system/bin/init subcontext ....
    InitializeSubcontext();

    // 为解析init.rc中的action和service做准备
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    // 加载init.rc
    LoadBootScripts(am, sm);

      // cgroups:用于控制资源,cpuset相关
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");

      // 这里会启动ueventd,
    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    Keychords keychords;
    am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return {};
            },
            "KeychordInit");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

    // 充电模式下不挂载文件系统,也不启动核心系统服务
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    return 0;
}

可以看到第二个阶段依次做了这些事:

  • 加载init.rc配置文件

    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("/system/etc/init/hw/init.rc");
            if (!parser.ParseConfig("/system/etc/init")) {
                late_import_paths.emplace_back("/system/etc/init");
            }
            // late_import is available only in Q and earlier release. As we don't
            // have system_ext in those versions, skip late_import for system_ext.
            parser.ParseConfig("/system_ext/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);
        }
    }
    
    Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
        Parser parser;
    
        parser.AddSectionParser("service", std::make_unique<ServiceParser>(
                                                   &service_list, GetSubcontext(), std::nullopt));
        parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
        parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
    
        return parser;
    }

    这里会根据init.rc中的item类型创建对应的parser:

    • ActionParser
    • ServiceParser
    • ImportParser

    在加载完init.rc文件之后,会加载/{system,vendor,odm}/etc/init/ 下(Android设备中的目录)的所有rc配置文件:

    • /system/etc/init/ : 核心系统,比如SurfaceFlinger,MediaService,logd

      ITouch.rc                             hwservicemanager.rc            runtime_data.rc
      android.hidl.allocator@1.0-service.rc iGraphics.rc                   samba.rc
      atrace.rc                             incidentd.rc                   selinux_dmd.rc
      audioserver.rc                        init.huawei.iaware.a15.rc      servicemanager.rc
      balonglogcat.rc                       init.huawei.os.a15.rc          shex.rc
      blank_screen.rc                       init.huawei.os.common.rc       shutanim.rc
      bootanim.rc                           init.huawei.recovery.common.rc statsd.rc
      bootstat.rc                           init.system.rc                 storaged.rc
      cameraserver.rc                       installd.rc                    surfaceflinger.rc
      displayengineserver.rc                keystore.rc                    thermal-daemon.rc
      drmserver.rc                          lmkd.rc                        thermalservice.rc
      dubaid.rc                             logcatd_ext.rc                 tombstoned.rc
      dumpstate.rc                          logd.rc                        uncrypt.rc
      gatekeeperd.rc                        mdnsd.rc                       update_engine.rc
      gpuassistant.rc                       mediadrmserver.rc              usbd.rc
      healthd.rc                            mediaextractor.rc              vdc.rc
      hilogcat.rc                           mediametrics.rc                vold.rc
      hilogd.rc                             mediaserver.rc                 wait_for_keymaster.rc
      hiview.rc                             mtpd.rc                        wificond.rc
      hivrarserver.rc                       netd.rc                        xlogcat.rc
      hwhfd.rc                              perfetto.rc
      hwnffserver.rc                        racoon.rc
    • /vendor/etc/init/: SOC需要的核心功能和守护程序

      activity_recognition_service.rc
      android.hardware.bluetooth@1.0-service.rc
      android.hardware.cas@1.0-service.rc
      android.hardware.configstore@1.1-service.rc
      android.hardware.drm@1.0-service.rc
      android.hardware.drm@1.1-service.clearkey.rc
      android.hardware.drm@1.1-service.widevine.rc
      android.hardware.gatekeeper@1.0-service.rc
      android.hardware.graphics.allocator@2.0-service.rc
      android.hardware.graphics.composer@2.2-service.rc
      android.hardware.health@2.0-service.rc
      android.hardware.ir@1.0-service.rc
      android.hardware.keymaster@3.0-service.rc
      android.hardware.media.omx@1.0-service.rc
      android.hardware.memtrack@1.0-service.rc
      android.hardware.secure_element@1.0-service.rc
      android.hardware.thermal@1.0-service.rc
      android.hardware.usb@1.0-service.rc
      android.hardware.vr@1.0-service.rc
      aptouch_daemon.rc
      charger
      connectivity
      hinetmanager.rc
      hisecd.rc
      hw
      init.audio.rc
      init.balong_modem.rc
      init.connectivity.rc
      init.device.rc
      init.extmodem.rc
      init.hisi.rc
      init.ko.rc
      init.manufacture.rc
      init.performance.rc
      init.platform.rc
      init.post-fs-data.rc
      init.protocol.rc
      init.tee.rc
      init.vowifi.rc
      isplogcat.rc
      modemchr_service.rc
      recovery
      rild.rc
      vendor.huawei.graphics.displayeffect@1.0-service.rc
      vendor.huawei.hardware.audio@4.0-service.rc
      vendor.huawei.hardware.biometrics.fingerprint@2.1-service.rc
      vendor.huawei.hardware.biometrics.hwfacerecognize@1.1-service.rc
      vendor.huawei.hardware.dolby.dms@1.0-service.rc
      vendor.huawei.hardware.fm@1.0-service.rc
      vendor.huawei.hardware.gnss@1.2-service.rc
      vendor.huawei.hardware.graphics.mediacomm@2.0-service.rc
      vendor.huawei.hardware.hisupl@1.0-service.rc
      vendor.huawei.hardware.hivrar@1.3-service.rc
      vendor.huawei.hardware.hwdisplay.displayengine@1.2-service.rc
      vendor.huawei.hardware.hwdisplay@1.0-service.rc
      vendor.huawei.hardware.hwfactoryinterface@1.1-service.rc
      vendor.huawei.hardware.hwfs@1.0-service.rc
      vendor.huawei.hardware.hwhiview@1.0-service.rc
      vendor.huawei.hardware.hwsecurity-service.rc
      vendor.huawei.hardware.hwupdate@1.0-service.rc
      vendor.huawei.hardware.hwvibrator@1.0-service.rc
      vendor.huawei.hardware.iawareperf@1.0-service.rc
      vendor.huawei.hardware.irsl@1.0-service.rc
      vendor.huawei.hardware.kds@1.0-service.rc
      vendor.huawei.hardware.libteec@2.0-service.rc
      vendor.huawei.hardware.light@2.0-service.rc
      vendor.huawei.hardware.nfc@1.0-service.rc
      vendor.huawei.hardware.otasimlock@1.0-service.rc
      vendor.huawei.hardware.otherdevices@1.0-service.rc
      vendor.huawei.hardware.perfgenius@2.0-service.rc
      vendor.huawei.hardware.power@1.0-service.rc
      vendor.huawei.hardware.sensors@1.0-service.rc
      vendor.huawei.hardware.tp@1.0-service.rc
      vendor.huawei.hardware.wifi@1.1-service.rc
      vndservicemanager.rc
    • /odm/etc/init/ : 设备制造商使用,比如传感器和其他外设功能或者守护程序

      init.hisi.odm.rc 
      init.odm.rc
  • 在触发early_int 之前,配置cgroup,kptr_restrict,perf等限制资源使用的工具

    Cgroup 抽象层

  • 触发early init,创建一些文件目录,挂载一些文件系统等,并启动ueventd

    on early-init
        start ueventd
  • 触发init,修改权限并挂载binderfs,启动logd.lmkd,servicemanager,hwservicemanager,vndservicemanager等

    on init
        # Mount binderfs
        mkdir /dev/binderfs
        mount binder binder /dev/binderfs stats=global
        chmod 0755 /dev/binderfs
    
        symlink /dev/binderfs/binder /dev/binder
        symlink /dev/binderfs/hwbinder /dev/hwbinder
        symlink /dev/binderfs/vndbinder /dev/vndbinder
    
        chmod 0666 /dev/binderfs/hwbinder
        chmod 0666 /dev/binderfs/binder
        chmod 0666 /dev/binderfs/vndbinder
    
        # Start logd before any other services run to ensure we capture all of their logs.
        start logd
        # Start lmkd before any other services run so that it can register them
        chown root system /sys/module/lowmemorykiller/parameters/adj
        chmod 0664 /sys/module/lowmemorykiller/parameters/adj
        chown root system /sys/module/lowmemorykiller/parameters/minfree
        chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
        start lmkd
    
        # Start essential services.
        start servicemanager
        start hwservicemanager
        start vndservicemanager

    低内存终止守护进程 (lmkd)

    • 挂载binderfs

      Android的IPC机制是通过/dev节点从用户空间访问的.一般会分配三个节点:

      • /dev/binder : 框架/应用进程之间的 IPC,使用 AIDL 接口
      • /dev/hwbinder : 框架/供应商进程之间的 IPC,使用 HIDL 接口
        供应商进程之间的 IPC,使用 HIDL 接口
      • /dev/vndbinder : 供应商/供应商进程之间的 IPC,使用 AIDL 接口
    • 启动logd,即Android的日志进程

    • 启动servicemanager,hwservicemanager,vndservicemanager

      Android O之前,Binder服务通过servicemanager注册,Android O开始binder节点分成了三种,因此对应的servicemanager也分成了三种.

    • 判断启动模式,如果是充电模式,在调用charger程序

      charger是关机模式下执行的程序,用于显示充电动画,可参考Android运行状况

    • 正常模式下启动,则挂载文件系统,并启动核心系统服务

      核心服务,即那些标记 class core的服务,比如init程序入口的ueventd:

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

      还有就是surfaceFlinger等:

      service surfaceflinger /system/bin/surfaceflinger
          class core animation
          user system
          group graphics drmrpc readproc
          onrestart restart zygote
          onrestart restart gpuassistant
          writepid /dev/stune/foreground/tasks
          socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
          socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
          socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
    • 启动zygote相关进程

      late-init中会触发:

          trigger zygote-start
      on zygote-start && property:ro.crypto.state=unencrypted
          # A/B update verifier that marks a successful boot.
          exec_start update_verifier_nonencrypted
          start netd
          start zygote
          start zygote_secondary
      
      on zygote-start && property:ro.crypto.state=unsupported
          # A/B update verifier that marks a successful boot.
          exec_start update_verifier_nonencrypted
          start netd
          start zygote
          start zygote_secondary
      
      on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
          # A/B update verifier that marks a successful boot.
          exec_start update_verifier_nonencrypted
          start netd
          start zygote
          start zygote_secondary

      zygote_secondary是针对同时支持64位,32位架构的配置:

      service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
          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/power/state on
          onrestart restart audioserver
          onrestart restart cameraserver
          onrestart restart media
          onrestart restart netd
          onrestart restart wificond
          writepid /dev/cpuset/foreground/tasks
      
      service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
          class main
          priority -20
          user root
          group root readproc reserved_disk
          socket zygote_secondary stream 660 root system
          socket usap_pool_secondary stream 660 root system
          onrestart restart zygote
          writepid /dev/cpuset/foreground/tasks

总结

  • init入口中包含5个分支:

    • ueventd : 实际上就是init程序的软连接,在init.rc的early-init阶段启动

    • selinux_setup : FirstStageMain 中通过 /system/bin/init selinux_setup 启动

    • subcontext : SecondStageMain 中启动

              auto init_path = GetExecutablePath();
              auto child_fd_string = std::to_string(child_fd);
              const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
                                    child_fd_string.c_str(), nullptr};
              execv(init_path.data(), const_cast<char**>(args));
    • second_stage : 在selinux_setp启动完之后执行

    • fisrt_stage : 默认首先执行

  • 第二个阶段,首先加载int.rc配置,然后再依次在Android设备中查找以下三种配置,并加载:

    • /system/etc/init/
    • /vendor/etc/init/
    • /odm/etc/init/
  • init.rc各个过程中会挂载文件系统,包括binderfs,创建各种文件目录,并分配权限

  • 启动servce manager,Android O开始分成三种,对应三种binder节点,给不同的层使用


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
Linux查看进程和线程信息 Linux查看进程和线程信息
psps -T -p 查看进程下所有线程信息 top 查看整体线程情况 top -H 查看指定进程的线程运行情况 top -H -p 查看当前活跃的进程 top
2020-06-24
下一篇 
TLS-Pinning TLS-Pinning
什么是SSL/TLS Pinning?将服务端提供的TLS/SSL证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置证书和服务器端证书的内容,以确定这个连接的合法性. 我这里把Pinning翻译成“固定” Pinni
2020-06-21
  目录