为您推荐:
精华内容
最热下载
问答
  • 当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。 阅读本文大约需要花费50分钟。 文章的内容主要...

    摘要:init进程是linux系统中用户空间的第一个进程,进程号为1.当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。

    阅读本文大约需要花费50分钟。

    文章的内容主要还是从源码进行分析,虽然又臭又长,但是如果想要学习Android系统源码,这是必要走的路,没有捷径。

    相对于碎片学习,我更倾向于静下心来花费1个小时认真的学习一段内容。

    文章首发微信公众号:IngresGe

    专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

    欢迎关注我的公众号!

    [Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

    [Android取经之路] 系列文章:

    《系统启动篇》

    1. Android系统架构
    2. Android是怎么启动的
    3. Android 10.0系统启动之init进程
    4. Android10.0系统启动之Zygote进程
    5. Android 10.0 系统启动之SystemServer进程
    6. Android 10.0 系统服务之ActivityMnagerService
    7. Android10.0系统启动之Launcher(桌面)启动流程
    8. Android10.0应用进程创建过程以及Zygote的fork流程
    9. Android 10.0 PackageManagerService(一)工作原理及启动流程
    10. Android 10.0 PackageManagerService(二)权限扫描
    11. Android 10.0 PackageManagerService(三)APK扫描
    12. Android 10.0 PackageManagerService(四)APK安装流程

    《日志系统篇》

    1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
    2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
    3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
    4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

    《Binder通信原理》

    1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
    2. Android10.0 Binder通信原理(二)-Binder入门篇
    3. Android10.0 Binder通信原理(三)-ServiceManager篇
    4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析
    5. Android10.0 Binder通信原理(五)-Binder驱动分析
    6. Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
    7. Android10.0 Binder通信原理(七)-Framework binder示例
    8. Android10.0 Binder通信原理(八)-Framework层分析
    9. Android10.0 Binder通信原理(九)-AIDL Binder示例
    10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
    11. Android10.0 Binder通信原理(十一)-Binder总结

      《HwBinder通信原理》

    1. HwBinder入门篇-Android10.0 HwBinder通信原理(一)
    2.  HIDL详解-Android10.0 HwBinder通信原理(二)
    3. HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
    4. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
    5. HwServiceManager篇-Android10.0 HwBinder通信原理(五)
    6. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
    7. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
    8. JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
    9. JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
    10. HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
    11. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

    《编译原理》

    1. 编译系统入门篇-Android10.0编译系统(一)
    2. 编译环境初始化-Android10.0编译系统(二)
    3. make编译过程-Android10.0编译系统(三)
    4. Image打包流程-Android10.0编译系统(四)
    5. Kati详解-Android10.0编译系统(五)

    Android init 启动进程主要分三个阶段分析

    1. 概述,Init如何被启动
    2. Init进程启动的源码分析
    3. rc语法分析

     

    1.概述:

    init进程是linux系统中用户空间的第一个进程,进程号为1.

    当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。

     

    init进程被赋予了很多重要工作,init进程启动主要分为两个阶段:

    第一个阶段完成以下内容:

    • ueventd/watchdogd跳转及环境变量设置
    • 挂载文件系统并创建目录
    • 初始化日志输出、挂载分区设备
    • 启用SELinux安全策略
    • 开始第二阶段前的准备

    第二个阶段完成以下内容:

    • 初始化属性系统
    • 执行SELinux第二阶段并恢复一些文件安全上下文
    • 新建epoll并初始化子进程终止信号处理函数
    • 设置其他系统属性并开启属性服务

    2.架构

    2.1 Init进程如何被启动?

    Init进程是在Kernel启动后,启动的第一个用户空间进程,PID为1。

    kernel_init启动后,完成一些init的初始化操作,然后去系统根目录下依次找ramdisk_execute_command和execute_command设置的应用程序,如果这两个目录都找不到,就依次去根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动,只要这些应用程序有一个启动了,其他就不启动了。

    Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接执行init这个文件。

     

    2.2Init进程启动后,做了哪些事?

    Init进程启动后,首先挂载文件系统、再挂载相应的分区,启动SELinux安全策略,启动属性服务,解析rc文件,并启动相应属性服务进程,初始化epoll,依次设置signal、property、keychord这3个fd可读时相对应的回调函数。进入无线循环,用来响应各个进程的变化与重建。

     

     

    3.kernel启动init进程 源码分析

     

    3.1  kernel_init

    kernel/msm-4.19/init/main.c

    kernel/msm-4.19/init/main.c
    kernel_init()
      |
    run_init_process(ramdisk_execute_command)  //运行可执行文件,启动init进程
    static int __ref kernel_init(void *unused)
    {
    	kernel_init_freeable(); //进行init进程的一些初始化操作
    	/* need to finish all async __init code before freeing the memory */
    	async_synchronize_full();// 等待所有异步调用执行完成,,在释放内存前,必须完成所有的异步 __init 代码
    	free_initmem();// 释放所有init.* 段中的内存
    	mark_rodata_ro(); //arm64空实现
    	system_state = SYSTEM_RUNNING;// 设置系统状态为运行状态
    	numa_default_policy(); // 设定NUMA系统的默认内存访问策略
    
    	flush_delayed_fput(); // 释放所有延时的struct file结构体
    
    	if (ramdisk_execute_command) { //ramdisk_execute_command的值为"/init"
    		if (!run_init_process(ramdisk_execute_command)) //运行根目录下的init程序
    			return 0;
    		pr_err("Failed to execute %s\n", ramdisk_execute_command);
    	}
    
    	/*
    	 * We try each of these until one succeeds.
    	 *
    	 * The Bourne shell can be used instead of init if we are
    	 * trying to recover a really broken machine.
    	 */
    	if (execute_command) { //execute_command的值如果有定义就去根目录下找对应的应用程序,然后启动
    		if (!run_init_process(execute_command))
    			return 0;
    		pr_err("Failed to execute %s.  Attempting defaults...\n",
    			execute_command);
    	}
    	if (!run_init_process("/sbin/init") || //如果ramdisk_execute_command和execute_command定义的应用程序都没有找到,
    	//就到根目录下找 /sbin/init,/etc/init,/bin/init,/bin/sh 这四个应用程序进行启动
    
    	    !run_init_process("/etc/init") ||
    	    !run_init_process("/bin/init") ||
    	    !run_init_process("/bin/sh"))
    		return 0;
    
    	panic("No init found.  Try passing init= option to kernel. "
    	      "See Linux Documentation/init.txt for guidance.");
    }

    3.2  do_basic_setup

    kernel_init_freeable()
    |
    do_basic_setup()

     

     

     

    static void __init do_basic_setup(void)
    {
    	cpuset_init_smp();//针对SMP系统,初始化内核control group的cpuset子系统。
    	usermodehelper_init();// 创建khelper单线程工作队列,用于协助新建和运行用户空间程序
    	shmem_init();// 初始化共享内存
    	driver_init();// 初始化设备驱动
    	init_irq_proc();//创建/proc/irq目录, 并初始化系统中所有中断对应的子目录
    	do_ctors();// 执行内核的构造函数
    	usermodehelper_enable();// 启用usermodehelper
    	do_initcalls();//遍历initcall_levels数组,调用里面的initcall函数,这里主要是对设备、驱动、文件系统进行初始化,
    	//之所有将函数封装到数组进行遍历,主要是为了好扩展
    
    	random_int_secret_init();//初始化随机数生成池
    }

     

     

     

     

    4. Init 进程启动源码分析

    我们主要是分析Android Q(10.0) 的init的代码。

     

    涉及源码文件:

    platform/system/core/init/main.cpp
    platform/system/core/init/init.cpp
    platform/system/core/init/ueventd.cpp
    platform/system/core/init/selinux.cpp
    platform/system/core/init/subcontext.cpp
    platform/system/core/base/logging.cpp
    platform/system/core/init/first_stage_init.cpp
    platform/system/core/init/first_stage_main.cpp
    platform/system/core/init/first_stage_mount.cpp
    platform/system/core/init/keyutils.h
    platform/system/core/init/property_service.cpp
    platform/external/selinux/libselinux/src/label.c
    platform/system/core/init/signal_handler.cpp
    platform/system/core/init/service.cpp

    4.1 Init 进程入口

    前面已经通过kernel_init,启动了init进程,init进程属于一个守护进程,准确的说,它是Linux系统中用户控制的第一个进程,它的进程号为1。它的生命周期贯穿整个Linux内核运行的始终。Android中所有其它的进程共同的鼻祖均为init进程。

    可以通过"adb shell ps |grep init" 的命令来查看init的进程号。

    Android Q(10.0) 的init入口函数由原先的init.cpp 调整到了main.cpp,把各个阶段的操作分离开来,使代码更加简洁命令,接下来我们就从main函数开始学习。

     [system/core/init/main.cpp]

    /*
     * 1.第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
     * 2.main函数有四个参数入口,
     *一是参数中有ueventd,进入ueventd_main
     *二是参数中有subcontext,进入InitLogging 和SubcontextMain
     *三是参数中有selinux_setup,进入SetupSelinux
     *四是参数中有second_stage,进入SecondStageMain
     *3.main的执行顺序如下:
       *  (1)ueventd_main    init进程创建子进程ueventd,
       *      并将创建设备节点文件的工作托付给ueventd,ueventd通过两种方式创建设备节点文件
       *  (2)FirstStageMain  启动第一阶段
       *  (3)SetupSelinux     加载selinux规则,并设置selinux日志,完成SELinux相关工作
       *  (4)SecondStageMain  启动第二阶段
     */
    int main(int argc, char** argv) {
        //当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1
        //1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一些列工作
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);
        }
    
       //当传入的参数个数大于1时,执行下面的几个操作
        if (argc > 1) {
            //参数为subcontext,初始化日志系统,
            if (!strcmp(argv[1], "subcontext")) {
                android::base::InitLogging(argv, &android::base::KernelLogger);
                const BuiltinFunctionMap function_map;
                return SubcontextMain(argc, argv, &function_map);
            }
    
          //参数为“selinux_setup”,启动Selinux安全策略
            if (!strcmp(argv[1], "selinux_setup")) {
                return SetupSelinux(argv);
            }
           //参数为“second_stage”,启动init进程第二阶段
            if (!strcmp(argv[1], "second_stage")) {
                return SecondStageMain(argc, argv);
            }
        }
     // 默认启动init进程第一阶段
        return FirstStageMain(argc, argv);
    }
    

    4.2 ueventd_main

    代码路径:platform/system/core/init/ueventd.cpp

    Android根文件系统的镜像中不存在“/dev”目录,该目录是init进程启动后动态创建的。

    因此,建立Android中设备节点文件的重任,也落在了init进程身上。为此,init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。

    ueventd通过两种方式创建设备节点文件。

    第一种方式对应“冷插拔”(Cold Plug),即以预先定义的设备信息为基础,当ueventd启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。

    第二种方式对应“热插拔”(Hot Plug),即在系统运行中,当有设备插入USB端口时,ueventd就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。

     

    int ueventd_main(int argc, char** argv) {
        //设置新建文件的默认值,这个与chmod相反,这里相当于新建文件后的权限为666
        umask(000); 
    
        //初始化内核日志,位于节点/dev/kmsg, 此时logd、logcat进程还没有起来,
        //采用kernel的log系统,打开的设备节点/dev/kmsg, 那么可通过cat /dev/kmsg来获取内核log。
        android::base::InitLogging(argv, &android::base::KernelLogger);
    
        //注册selinux相关的用于打印log的回调函数
        SelinuxSetupKernelLogging(); 
        SelabelInitialize();
    
        //解析xml,根据不同SOC厂商获取不同的hardware rc文件
        auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",
                                                  "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
    
        //冷启动
        if (access(COLDBOOT_DONE, F_OK) != 0) {
            ColdBoot cold_boot(uevent_listener, uevent_handlers);
            cold_boot.Run();
        }
        for (auto& uevent_handler : uevent_handlers) {
            uevent_handler->ColdbootDone();
        }
    
        //忽略子进程终止信号
        signal(SIGCHLD, SIG_IGN);
        // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
        // for SIGCHLD above.
           //在最后一次调用waitpid()和为上面的sigchld设置SIG_IGN之间退出的获取和挂起的子级
        while (waitpid(-1, nullptr, WNOHANG) > 0) {
        }
    
        //监听来自驱动的uevent,进行“热插拔”处理
        uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
            for (auto& uevent_handler : uevent_handlers) {
                uevent_handler->HandleUevent(uevent); //热启动,创建设备
            }
            return ListenerAction::kContinue;
        });
        return 0;
    }
    

    4.3 init 进程启动第一阶段

    代码路径:platform\system\core\init\first_stage_init.cpp

    init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略

    第一阶段完成以下内容:

    /* 01. 创建文件系统目录并挂载相关的文件系统 */

    /* 02. 屏蔽标准的输入输出/初始化内核log系统 */

     

    4.3.1 FirstStageMain

     

    int FirstStageMain(int argc, char** argv) {
        //init crash时重启引导加载程序
        //这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
        if (REBOOT_BOOTLOADER_ON_PANIC) {
            InstallRebootSignalHandlers();
        }
        //清空文件权限
        umask(0);
    
        CHECKCALL(clearenv());
        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    
        //在RAM内存上获取基本的文件系统,剩余的被rc文件所用
        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));
    #define MAKE_STR(x) __STRING(x)
        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
    #undef MAKE_STR
    
        // 非特权应用不能使用Andrlid cmdline
        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)));
    
    
        //这对于日志包装器是必需的,它在ueventd运行之前被调用
        CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
        CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
    
    
        //在第一阶段挂在tmpfs、mnt/vendor、mount/product分区。其他的分区不需要在第一阶段加载,
        //只需要在第二阶段通过rc文件解析来加载。
        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                        "mode=0755,uid=0,gid=1000"));
        
        //创建可供读写的vendor目录
        CHECKCALL(mkdir("/mnt/vendor", 0755));
        // /mnt/product is used to mount product-specific partitions that can not be
        // part of the product partition, e.g. because they are mounted read-write.
        CHECKCALL(mkdir("/mnt/product", 0755));
    
        // 挂载APEX,这在Android 10.0中特殊引入,用来解决碎片化问题,类似一种组件方式,对Treble的增强,
        // 不写谷歌特殊更新不需要完整升级整个系统版本,只需要像升级APK一样,进行APEX组件升级
        CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                        "mode=0755,uid=0,gid=0"));
    
        // /debug_ramdisk is used to preserve additional files from the debug ramdisk
        CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                        "mode=0755,uid=0,gid=0"));
    #undef CHECKCALL
    
        //把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
        SetStdioToDevNull(argv);
        //在/dev目录下挂载好 tmpfs 以及 kmsg 
        //这样就可以初始化 /kernel Log 系统,供用户打印log
        InitKernelLogging(argv);
    
        ...
    
        /* 初始化一些必须的分区
         *主要作用是去解析/proc/device-tree/firmware/android/fstab,
         * 然后得到"/system", "/vendor", "/odm"三个目录的挂载信息
         */
        if (!DoFirstStageMount()) {
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }
    
        struct stat new_root_info;
        if (stat("/", &new_root_info) != 0) {
            PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
            old_root_dir.reset();
        }
    
        if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
            FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
        }
    
        SetInitAvbVersionInRecovery();
    
        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
    
        //启动init进程,传入参数selinux_steup
        // 执行命令: /system/bin/init selinux_setup
        const char* path = "/system/bin/init";
        const char* args[] = {path, "selinux_setup", nullptr};
        execv(path, const_cast<char**>(args));
        PLOG(FATAL) << "execv(\"" << path << "\") failed";
    
        return 1;
    }
    

    4.4 加载SELinux规则

    SELinux是「Security-Enhanced Linux」的简称,是美国国家安全局「NSA=The National Security Agency」

    和SCC(Secure Computing Corporation)开发的 Linux的一个扩张强制访问控制安全模块。

    在这种访问控制体系的限制下,进程只能访问那些在他的任务中所需要文件。

    selinux有两种工作模式:

    1. permissive,所有的操作都被允许(即没有MAC),但是如果违反权限的话,会记录日志,一般eng模式用
    2. enforcing,所有操作都会进行权限检查。一般user和user-debug模式用

    不管是security_setenforce还是security_getenforce都是去操作/sys/fs/selinux/enforce 文件, 0表示permissive 1表示enforcing

     

    4.4.1 SetupSelinux

    说明:初始化selinux,加载SELinux规则,配置SELinux相关log输出,并启动第二阶段

    代码路径: platform\system\core\init\selinux.cpp

    /*此函数初始化selinux,然后执行init以在init selinux中运行*/
    int SetupSelinux(char** argv) {
           //初始化Kernel日志
        InitKernelLogging(argv);
    
           // Debug版本init crash时重启引导加载程序
        if (REBOOT_BOOTLOADER_ON_PANIC) {
            InstallRebootSignalHandlers();
        }
    
        //注册回调,用来设置需要写入kmsg的selinux日志
        SelinuxSetupKernelLogging();
       
         //加载SELinux规则
        SelinuxInitialize();
    
        /*
           *我们在内核域中,希望转换到init域。在其xattrs中存储selabel的文件系统(如ext4)不需要显式restorecon,
           *但其他文件系统需要。尤其是对于ramdisk,如对于a/b设备的恢复映像,这是必需要做的一步。
           *其实就是当前在内核域中,在加载Seliux后,需要重新执行init切换到C空间的用户态
           */
        if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
            PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
        }
    
      //准备启动innit进程,传入参数second_stage
        const char* path = "/system/bin/init";
        const char* args[] = {path, "second_stage", nullptr};
        execv(path, const_cast<char**>(args));
    
        /*
           *执行 /system/bin/init second_stage, 进入第二阶段
           */
        PLOG(FATAL) << "execv(\"" << path << "\") failed";
    
        return 1;
    }
    

    4.4.2 SelinuxInitialize()

     

     

    /*加载selinux 规则*/
    void SelinuxInitialize() {
        LOG(INFO) << "Loading SELinux policy";
        if (!LoadPolicy()) {
            LOG(FATAL) << "Unable to load SELinux policy";
        }
    
        //获取当前Kernel的工作模式
        bool kernel_enforcing = (security_getenforce() == 1);
    
        //获取工作模式的配置
        bool is_enforcing = IsEnforcing();
    
        //如果当前的工作模式与配置的不同,就将当前的工作模式改掉
        if (kernel_enforcing != is_enforcing) {
            if (security_setenforce(is_enforcing)) {
                PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
                            << ") failed";
            }
        }
    
        if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
            LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
        }
    }
    /*
     *加载SELinux规则
     *这里区分了两种情况,这两种情况只是区分从哪里加载安全策略文件,
     *第一个是从 /vendor/etc/selinux/precompiled_sepolicy 读取,
     *第二个是从 /sepolicy 读取,他们最终都是调用selinux_android_load_policy_from_fd方法
     */
    bool LoadPolicy() {
        return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
    }
    

    4.5 init进程启动第二阶段

    第二阶段主要内容:

    1. 创建进程会话密钥并初始化属性系统
    2. 进行SELinux第二阶段并恢复一些文件安全上下文
    3. 新建epoll并初始化子进程终止信号处理函数,详细看第五节-信号处理
    4. 启动匹配属性的服务端, 详细查看第六节-属性服务
    5. 解析init.rc等文件,建立rc文件的action 、service,启动其他进程,详细查看第七节-rc文件解析

     

    4.5.1 SecondStageMain

     

    int SecondStageMain(int argc, char** argv) {
        /* 01. 创建进程会话密钥并初始化属性系统 */
        keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
    
        //创建 /dev/.booting 文件,就是个标记,表示booting进行中
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    
        // 初始化属性系统,并从指定文件读取属性
        property_init();
    
        /* 02. 进行SELinux第二阶段并恢复一些文件安全上下文 */
        	SelinuxRestoreContext();
    
        /* 03. 新建epoll并初始化子进程终止信号处理函数 */
        Epoll epoll;
        if (auto result = epoll.Open(); !result) {
            PLOG(FATAL) << result.error();
        }
    
        	     InstallSignalFdHandler(&epoll);
    
        /* 04. 设置其他系统属性并开启系统属性服务*/
        StartPropertyService(&epoll);
    
        	   /* 05 解析init.rc等文件,建立rc文件的action 、service,启动其他进程*/
        ActionManager& am = ActionManager::GetInstance();
        ServiceList& sm = ServiceList::GetInstance();
        LoadBootScripts(am, sm);
    }
    

    代码流程详细解析:

    int SecondStageMain(int argc, char** argv) {
        /* 
        *init crash时重启引导加载程序
        *这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
        */
        if (REBOOT_BOOTLOADER_ON_PANIC) {
            InstallRebootSignalHandlers();
        }
    
        //把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
        SetStdioToDevNull(argv);
        //在/dev目录下挂载好 tmpfs 以及 kmsg 
        //这样就可以初始化 /kernel Log 系统,供用户打印log
        InitKernelLogging(argv);
        LOG(INFO) << "init second stage started!";
    
        // 01. 创建进程会话密钥并初始化属性系统
        keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
    
        //创建 /dev/.booting 文件,就是个标记,表示booting进行中
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    
        // 初始化属性系统,并从指定文件读取属性
        property_init();
    
        /*
         * 1.如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
         * 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数,
         */
        process_kernel_dt(); // 处理 DT属性
        process_kernel_cmdline(); // 处理命令行属性
    
        // 处理一些其他的属性
        export_kernel_boot_props();
    
        // Make the time that init started available for bootstat to log.
        property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
        property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
    
        // Set libavb version for Framework-only OTA match in Treble build.
        const char* avb_version = getenv("INIT_AVB_VERSION");
        if (avb_version) property_set("ro.boot.avb_version", avb_version);
    
        // See if need to load debug props to allow adb root, when the device is unlocked.
        const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
        if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
            load_debug_prop = "true"s == force_debuggable_env;
        }
    
        // 基于cmdline设置memcg属性
        bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
        if (memcg_enabled) {
           // root memory control cgroup
           mkdir("/dev/memcg", 0700);
           chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
           mount("none", "/dev/memcg", "cgroup", 0, "memory");
           // app mem cgroups, used by activity manager, lmkd and zygote
           mkdir("/dev/memcg/apps/",0755);
           chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
           mkdir("/dev/memcg/system",0550);
           chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
        }
    
        // 清空这些环境变量,之前已经存到了系统属性中去了
        unsetenv("INIT_STARTED_AT");
        unsetenv("INIT_SELINUX_TOOK");
        unsetenv("INIT_AVB_VERSION");
        unsetenv("INIT_FORCE_DEBUGGABLE");
    
        // Now set up SELinux for second stage.
        SelinuxSetupKernelLogging();
        SelabelInitialize();
    
        /*
         * 02. 进行SELinux第二阶段并恢复一些文件安全上下文 
         * 恢复相关文件的安全上下文,因为这些文件是在SELinux安全机制初始化前创建的,
         * 所以需要重新恢复上下文
         */
        SelinuxRestoreContext();
    
       /*
        * 03. 新建epoll并初始化子进程终止信号处理函数
        *  创建epoll实例,并返回epoll的文件描述符
        */
        Epoll epoll;
        if (auto result = epoll.Open(); !result) {
            PLOG(FATAL) << result.error();
        }
    
        /* 
         *主要是创建handler处理子进程终止信号,注册一个signal到epoll进行监听
         *进行子继承处理
         */
        InstallSignalFdHandler(&epoll);
    
        // 进行默认属性配置相关的工作
        property_load_boot_defaults(load_debug_prop);
        UmountDebugRamdisk();
        fs_mgr_vendor_overlay_mount_all();
        export_oem_lock_status();
    
        /*
         *04. 设置其他系统属性并开启系统属性服务
         */
        StartPropertyService(&epoll);
        MountHandler mount_handler(&epoll);
    
        //为USB存储设置udc Contorller, sys/class/udc
        set_usb_controller();
    
        // 匹配命令和函数之间的对应关系
        const BuiltinFunctionMap function_map;
        Action::set_function_map(&function_map);
    
        if (!SetupMountNamespaces()) {
            PLOG(FATAL) << "SetupMountNamespaces failed";
        }
    
        // 初始化文件上下文
        subcontexts = InitializeSubcontexts();
    
       /*
         *05 解析init.rc等文件,建立rc文件的action 、service,启动其他进程
         */
        ActionManager& am = ActionManager::GetInstance();
        ServiceList& sm = ServiceList::GetInstance();
    
        LoadBootScripts(am, sm);
    
        // Turning this on and letting the INFO logging be discarded adds 0.2s to
        // Nexus 9 boot time, so it's disabled by default.
        if (false) DumpState();
    
        // 当GSI脚本running时,确保GSI状态可用.
        if (android::gsi::IsGsiRunning()) {
            property_set("ro.gsid.image_running", "1");
        } else {
            property_set("ro.gsid.image_running", "0");
        }
    
    
        am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    
        // 执行rc文件中触发器为 on early-init 的语句
        am.QueueEventTrigger("early-init");
    
        // 等冷插拔设备初始化完成
        am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    
        // 开始查询来自 /dev的 action
        am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
        am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
        am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    
        // 设备组合键的初始化操作
        Keychords keychords;
        am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return Success();
            },
            "KeychordInit");
    
        //在屏幕上显示Android 静态LOGO
        am.QueueBuiltinAction(console_init_action, "console_init");
    
        // 执行rc文件中触发器为on init的语句
        am.QueueEventTrigger("init");
    
        // Starting the BoringSSL self test, for NIAP certification compliance.
        am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
    
        // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
        // wasn't ready immediately after wait_for_coldboot_done
        am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    
        // Initialize binder before bringing up other system services
        am.QueueBuiltinAction(InitBinder, "InitBinder");
    
        // 当设备处于充电模式时,不需要mount文件系统或者启动系统服务
        // 充电模式下,将charger假如执行队列,否则把late-init假如执行队列
        std::string bootmode = GetProperty("ro.bootmode", "");
        if (bootmode == "charger") {
            am.QueueEventTrigger("charger");
        } else {
            am.QueueEventTrigger("late-init");
        }
    
        // 基于属性当前状态 运行所有的属性触发器.
        am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
    
        while (true) {
            // By default, sleep until something happens.
            auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
    
            if (do_shutdown && !shutting_down) {
                do_shutdown = false;
                if (HandlePowerctlMessage(shutdown_command)) {
                    shutting_down = true;
                }
            }
    
    //依次执行每个action中携带command对应的执行函数
            if (!(waiting_for_prop || Service::is_exec_service_running())) {
                am.ExecuteOneCommand();
            }
            if (!(waiting_for_prop || Service::is_exec_service_running())) {
                if (!shutting_down) {
                    auto next_process_action_time = HandleProcessActions();
    
                    // If there's a process that needs restarting, wake up in time for that.
                    if (next_process_action_time) {
                        epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                                *next_process_action_time - boot_clock::now());
                        if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                    }
                }
    
                // If there's more work to do, wake up again immediately.
                if (am.HasMoreCommands()) epoll_timeout = 0ms;
            }
    
            // 循环等待事件发生
            if (auto result = epoll.Wait(epoll_timeout); !result) {
                LOG(ERROR) << result.error();
            }
        }
    
        return 0;
    }

     

    5. 信号处理

    init是一个守护进程,为了防止init的子进程成为僵尸进程(zombie process),需要init在子进程在结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。

    子进程重启流程如下图所示:

     

    信号处理主要工作:

    • 初始化信号signal句柄
    • 循环处理子进程
    • 注册epoll句柄
    • 处理子进程终止

    注:  EPOLL类似于POLL,是Linux中用来做事件触发的,跟EventBus功能差不多。linux很长的时间都在使用select来做事件触发,它是通过轮询来处理的,轮询的fd数目越多,自然耗时越多,对于大量的描述符处理,EPOLL更有优势

     

    5.1 InstallSignalFdHandler

    在linux当中,父进程是通过捕捉SIGCHLD信号来得知子进程运行结束的情况,SIGCHLD信号会在子进程终止的时候发出,了解这些背景后,我们来看看init进程如何处理这个信号。

    • 首先,新建一个sigaction结构体,sa_handler是信号处理函数,指向内核指定的函数指针SIG_DFL和Android 9.0及之前的版本不同,这里不再通过socket的读写句柄进行接收信号,改成了内核的信号处理函数SIG_DFL。
    • 然后,sigaction(SIGCHLD, &act, nullptr) 这个是建立信号绑定关系,也就是说当监听到SIGCHLD信号时,由act这个sigaction结构体处理
    • 最后,RegisterHandler 的作用就是signal_read_fd(之前的s[1])收到信号,触发handle_signal

    终上所述,InstallSignalFdHandler函数的作用就是,接收到SIGCHLD信号时触发HandleSignalFd进行信号处理

                                       信号处理示意图:

     

     

    代码路径:platform/system/core/init.cpp

    说明:该函数主要的作用是初始化子进程终止信号处理过程

    static void InstallSignalFdHandler(Epoll* epoll) {
     
        // SA_NOCLDSTOP使init进程只有在其子进程终止时才会受到SIGCHLD信号
        const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
        sigaction(SIGCHLD, &act, nullptr);
    
        sigset_t mask;
        sigemptyset(&mask);
        sigaddset(&mask, SIGCHLD); 
    
        if (!IsRebootCapable()) {
            // 如果init不具有 CAP_SYS_BOOT的能力,则它此时正值容器中运行
            // 在这种场景下,接收SIGTERM 将会导致系统关闭
            sigaddset(&mask, SIGTERM);
        }
    
        if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
            PLOG(FATAL) << "failed to block signals";
        }
    
        // 注册处理程序以解除对子进程中的信号的阻止
        const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
        if (result != 0) {
            LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
        }
    
        //创建信号句柄
        signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
        if (signal_fd == -1) {
            PLOG(FATAL) << "failed to create signalfd";
        }
    
        //信号注册,当signal_fd收到信号时,触发HandleSignalFd
        if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
            LOG(FATAL) << result.error();
        }
    }

    5.2 RegisterHandler

     

    代码路径:/platform/system/core/epoll.cpp

    说明:信号注册,把fd句柄加入到 epoll_fd_的监听队列中

    Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
        if (!events) {
            return Error() << "Must specify events";
        }
        auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
        if (!inserted) {
            return Error() << "Cannot specify two epoll handlers for a given FD";
        }
        epoll_event ev;
        ev.events = events;
        // std::map's iterators do not get invalidated until erased, so we use the
        // pointer to the std::function in the map directly for epoll_ctl.
        ev.data.ptr = reinterpret_cast<void*>(&it->second);
        // 将fd的可读事件加入到epoll_fd_的监听队列中
        if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
            Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
            epoll_handlers_.erase(fd);
            return result;
        }
        return {};
    }
    

     

    5.3 HandleSignalFd

     

    代码路径:platform/system/core/init.cpp

    说明:监控SIGCHLD信号,调用 ReapAnyOutstandingChildren 来 终止出现问题的子进程

    static void HandleSignalFd() {
        signalfd_siginfo siginfo;
        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
        if (bytes_read != sizeof(siginfo)) {
            PLOG(ERROR) << "Failed to read siginfo from signal_fd";
            return;
        }
    
       //监控SIGCHLD信号
        switch (siginfo.ssi_signo) {
            case SIGCHLD:
                ReapAnyOutstandingChildren();
                break;
            case SIGTERM:
                HandleSigtermSignal(siginfo);
                break;
            default:
                PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
                break;
        }
    }

     

    5.4 ReapOneProcess

     

    代码路径:/platform/system/core/sigchld_handle.cpp

    说明:ReapOneProcess是最终的处理函数了,这个函数先用waitpid找出挂掉进程的pid,然后根据pid找到对应Service,

    最后调用Service的Reap方法清除资源,根据进程对应的类型,决定是否重启机器或重启进程

    void ReapAnyOutstandingChildren() {
        while (ReapOneProcess()) {
        }
    }
    
    static bool ReapOneProcess() {
        siginfo_t siginfo = {};
        //用waitpid函数获取状态发生变化的子进程pid
        //waitpid的标记为WNOHANG,即非阻塞,返回为正值就说明有进程挂掉了
        if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
            PLOG(ERROR) << "waitid failed";
            return false;
        }
    
        auto pid = siginfo.si_pid;
        if (pid == 0) return false;
    
        // 当我们知道当前有一个僵尸pid,我们使用scopeguard来清楚该pid
        auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
    
        std::string name;
        std::string wait_string;
        Service* service = nullptr;
    
        if (SubcontextChildReap(pid)) {
            name = "Subcontext";
        } else {
            //通过pid找到对应的service
            service = ServiceList::GetInstance().FindService(pid, &Service::pid);
    
            if (service) {
                name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
                if (service->flags() & SVC_EXEC) {
                    auto exec_duration = boot_clock::now() - service->time_started();
                    auto exec_duration_ms =
                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                    wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
                } else if (service->flags() & SVC_ONESHOT) {
                    auto exec_duration = boot_clock::now() - service->time_started();
                    auto exec_duration_ms =
                            std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
                                    .count();
                    wait_string = StringPrintf(" oneshot service took %f seconds in background",exec_duration_ms / 1000.0f);
                }
            } else {
                name = StringPrintf("Untracked pid %d", pid);
            }
        }
    
        if (siginfo.si_code == CLD_EXITED) {
            LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
        } else {
            LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
        }
    
        //没有找到service,说明已经结束了,退出
        if (!service) return true;
    
        service->Reap(siginfo);//清除子进程相关的资源
    
        if (service->flags() & SVC_TEMPORARY) {
            ServiceList::GetInstance().RemoveService(*service); //移除该service
        }
    
        return true;
    }

     

    6.属性服务

    我们在开发和调试过程中看到通过property_set可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,

    Android将属性的设置统一交由init进程管理,其他进程不能直接修改属性,而只能通知init进程来修改,而在这过程中,init进程可以进行权限控制,我们来看看具体的流程是什么

     

    6.1 property_init

    代码路径:platform/system/core/property_service.cpp

    说明:初始化属性系统,并从指定文件读取属性,并进行SELinux注册,进行属性权限控制

    清除缓存,这里主要是清除几个链表以及在内存中的映射,新建property_filename目录,这个目录的值为 /dev/_properties_

    然后就是调用CreateSerializedPropertyInfo加载一些系统属性的类别信息,最后将加载的链表写入文件并映射到内存

    void property_init() {
    
        //设置SELinux回调,进行权限控制
        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()) {
            LOG(FATAL) << "Failed to initialize property area";
        }
        if (!property_info_area.LoadDefaultPath()) {
            LOG(FATAL) << "Failed to load serialized property info file";
        }
    }

     

    通过CreateSerializedPropertyInfo 来加载以下目录的contexts

    1)与SELinux相关

    /system/etc/selinux/plat_property_contexts
    
    /vendor/etc/selinux/vendor_property_contexts
    
    /vendor/etc/selinux/nonplat_property_contexts
    
    /product/etc/selinux/product_property_contexts
    
    /odm/etc/selinux/odm_property_contexts

    2)与SELinux无关

    /plat_property_contexts
    
    /vendor_property_contexts
    
    /nonplat_property_contexts
    
    /product_property_contexts
    
    /odm_property_contexts

     

    6.2 StartPropertyService

    代码路径: platform/system/core/init.cpp

    说明:启动属性服务

    首先创建一个socket并返回文件描述符,然后设置最大并发数为8,其他进程可以通过这个socket通知init进程修改系统属性,

    最后注册epoll事件,也就是当监听到property_set_fd改变时调用handle_property_set_fd

    void StartPropertyService(Epoll* epoll) {
        property_set("ro.property_service.version", "2");
    
        //建立socket连接
        if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                       false, 0666, 0, 0, {})) {
            property_set_fd = *result;
        } else {
            PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
        }
    
        // 最大监听8个并发
        listen(property_set_fd, 8);
    
        // 注册property_set_fd,当收到句柄改变时,通过handle_property_set_fd来处理
        if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
            PLOG(FATAL) << result.error();
        }
    }

    6.3 handle_property_set_fd

    代码路径:platform/system/core/property_service.cpp

    说明:建立socket连接,然后从socket中读取操作信息,根据不同的操作类型,调用HandlePropertySet做具体的操作

    HandlePropertySet是最终的处理函数,以"ctl"开头的key就做一些Service的Start,Stop,Restart操作,其他的就是调用property_set进行属性设置,不管是前者还是后者,都要进行SELinux安全性检查,只有该进程有操作权限才能执行相应操作

     

    static void handle_property_set_fd() {
        static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
    
        // 等待客户端连接
        int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
        if (s == -1) {
            return;
        }
    
        ucred cr;
        socklen_t cr_size = sizeof(cr);
        // 获取连接到此socket的进程的凭据
        if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
            close(s);
            PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
            return;
        }
    
        // 建立socket连接
        SocketConnection socket(s, cr);
        uint32_t timeout_ms = kDefaultSocketTimeout;
    
        uint32_t cmd = 0;
        // 读取socket中的操作信息
        if (!socket.RecvUint32(&cmd, &timeout_ms)) {
            PLOG(ERROR) << "sys_prop: error while reading command from the socket";
            socket.SendUint32(PROP_ERROR_READ_CMD);
            return;
        }
    
        // 根据操作信息,执行对应处理,两者区别一个是以char形式读取,一个以String形式读取
        switch (cmd) {
        case PROP_MSG_SETPROP: {
            char prop_name[PROP_NAME_MAX];
            char prop_value[PROP_VALUE_MAX];
    
            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;
    
            std::string source_context;
            if (!socket.GetSourceContext(&source_context)) {
                PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
                return;
            }
    
            const auto& cr = socket.cred();
            std::string error;
            uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
            if (result != PROP_SUCCESS) {
                LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                           << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
            }
    
            break;
          }
    
        case PROP_MSG_SETPROP2: {
            std::string name;
            std::string value;
            if (!socket.RecvString(&name, &timeout_ms) ||
                !socket.RecvString(&value, &timeout_ms)) {
              PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
              socket.SendUint32(PROP_ERROR_READ_DATA);
              return;
            }
    
            std::string source_context;
            if (!socket.GetSourceContext(&source_context)) {
                PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
                socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
                return;
            }
    
            const auto& cr = socket.cred();
            std::string error;
            uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
            if (result != PROP_SUCCESS) {
                LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                           << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
            }
            socket.SendUint32(result);
            break;
          }
    
        default:
            LOG(ERROR) << "sys_prop: invalid command " << cmd;
            socket.SendUint32(PROP_ERROR_INVALID_CMD);
            break;
        }
    }
    

    7.第三阶段init.rc

    当属性服务建立完成后,init的自身功能基本就告一段落,接下来需要来启动其他的进程。但是init进程如何其他其他进程呢?其他进程都是一个二进制文件,我们可以直接通过exec的命令方式来启动,例如 ./system/bin/init second_stage,来启动init进程的第二阶段。但是Android系统有那么多的Native进程,如果都通过传exec在代码中一个个的来执行进程,那无疑是一个灾难性的设计。

    在这个基础上Android推出了一个init.rc的机制,即类似通过读取配置文件的方式,来启动不同的进程。接下来我们就来看看init.rc是如何工作的。

    init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本。

    init.rc在手机的目录:./init.rc

    init.rc主要包含五种类型语句:

    • Action
    • Command
    • Service
    • Option
    • Import

     

    7.1 Action

    动作表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个动作

    Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:

    • on early-init; 在初始化早期阶段触发;
    • on init; 在初始化阶段触发;
    • on late-init; 在初始化晚期阶段触发;
    • on boot/charger: 当系统启动/充电时触发;
    • on property:<key>=<value>: 当属性值满足条件时触发;

     

    7.2 Command

    command是action的命令列表中的命令,或者是service中的选项 onrestart 的参数命令,命令将在所属事件发生时被一个个地执行.

    下面列举常用的命令

    • class_start <service_class_name>: 启动属于同一个class的所有服务;
    • class_stop <service_class_name> : 停止指定类的服务
    • start <service_name>: 启动指定的服务,若已启动则跳过;
    • stop <service_name>: 停止正在运行的服务
    • setprop <name> <value>:设置属性值
    • mkdir <path>:创建指定目录
    • symlink <target> <sym_link>: 创建连接到<target>的<sym_link>符号链接;
    • write <path> <string>: 向文件path中写入字符串;
    • exec: fork并执行,会阻塞init进程直到程序完毕;
    • exprot <name> <name>:设定环境变量;
    • loglevel <level>:设置log级别
    • hostname <name> : 设置主机名
    • import <filename> :导入一个额外的init配置文件

     

    7.3 Service

    服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。

    命令:service <name><pathname> [ <argument> ]* <option> <option>

     

    参数

    含义

    <name>

    表示此服务的名称

    <pathname>

    此服务所在路径因为是可执行文件,所以一定有存储路径。

    <argument>

    启动服务所带的参数

    <option>

    对此服务的约束选项

    init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。

    例如: service servicemanager /system/bin/servicemanager代表的是服务名为servicemanager,服务执行的路径为/system/bin/servicemanager。

     

     

    7.4 Options

    Options是Service的可选项,与service配合使用

    • disabled: 不随class自动启动,只有根据service名才启动;
    • oneshot: service退出后不再重启;
    • user/group: 设置执行服务的用户/用户组,默认都是root;
    • class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;
    • onrestart:当服务重启时执行相应命令;
    • socket: 创建名为/dev/socket/<name>的socket
    • critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

    default: 意味着disabled=false,oneshot=false,critical=false。

     

    7.5 import

    用来导入其他的rc文件

    命令:import <filename>

     

    7.6 init.rc 解析过程

    7.6.1 LoadBootScripts

    代码路径:platform\system\core\init\init.cpp

    说明:如果没有特殊配置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文件

     

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

     

    Android7.0后,init.rc进行了拆分,每个服务都有自己的rc文件,他们基本上都被加载到/system/etc/init,/vendor/etc/init, /odm/etc/init等目录,等init.rc解析完成后,会来解析这些目录中的rc文件,用来执行相关的动作。

     

    代码路径:platform\system\core\init\init.cpp

    说明:创建Parser解析对象,例如service、on、import对象

     

    Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
        Parser parser;
    
        parser.AddSectionParser(
                "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
        parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
        parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
    
        return parser;
    }

    7.6.2 执行Action动作

    按顺序把相关Action加入触发器队列,按顺序为 early-init -> init -> late-init. 然后在循环中,执行所有触发器队列中Action带Command的执行函数。

     

    am.QueueEventTrigger("early-init");
    am.QueueEventTrigger("init");
    am.QueueEventTrigger("late-init");
    ...
    while (true) {
    if (!(waiting_for_prop || Service::is_exec_service_running())) {
                am.ExecuteOneCommand();
            }
    }

    7.6.2 Zygote启动

    从Android 5.0的版本开始,Android支持64位的编译,因此zygote本身也支持32位和64位。通过属性ro.zygote来控制不同版本的zygote进程启动。

    在init.rc的import段我们看到如下代码:

    import /init.${ro.zygote}.rc // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件
    


     init.rc位于/system/core/rootdir下。在这个路径下还包括四个关于zygote的rc文件。

    分别是init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc,由硬件决定调用哪个文件。

    这里拿64位处理器为例,init.zygote64.rc的代码如下所示

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
        class main		# class是一个option,指定zygote服务的类型为main
        	    priority -20
                user root
       	    group root readproc reserved_disk
        	    socket zygote stream 660 root system  # socket关键字表示一个option,创建一个名为dev/socket/zygote,类型为stream,权限为660的socket
       	    socket usap_pool_primary stream 660 root system
                onrestart write /sys/android_power/request_state wake # onrestart是一个option,说明在zygote重启时需要执行的command
        	    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 /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 解析:

    service zygote :init.zygote64.rc 中定义了一个zygote服务。 init进程就是通过这个service名称来创建zygote进程

    /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server解析:

    zygote这个服务,通过执行进行/system/bin/app_process64 并传入4个参数进行运行:

    • 参数1:-Xzygote 该参数将作为虚拟机启动时所需的参数
    • 参数2:/system/bin 代表虚拟机程序所在目录
    • 参数3:--zygote 指明以ZygoteInit.java类中的main函数作为虚拟机执行入口
    • 参数4:--start-system-server 告诉Zygote进程启动systemServer进程

    8.总结

    init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。

    init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些功能落实。

    init进行第三阶段主要是解析init.rc 来启动其他进程,进入无限循环,进行子进程实时监控。

     

    下一节我们来一起看看Android 10.0 的Zygote进程,这个JAVA进程的鼻祖是如何启动的。欢迎关注我,谢谢!

    微信公众号:IngresGe

    展开全文
    yiranfeng 2019-12-15 16:12:11
  • 旗浩 2021-11-30 18:00:56
  • 作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程

    相关文章
    Android系统架构与系统源码目录

    前言

    作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程。

    1.init简介

    init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init。本文将基于Android7.0源码来分析Init进程。

    2.引入init进程

    说到init进程,首先要提到Android系统启动流程的前几步:
    1.启动电源以及系统启动
    当电源按下时引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM,然后执行。
    2.引导程序Bootloader
    引导程序是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。
    3.linux内核启动
    内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
    4.init进程启动

    讲到第四步就发现我们这一节要讲的init进程了。关于Android系统启动流程的所有步骤会在本系列的最后一篇做讲解。

    3.init入口函数

    init的入口函数为main,代码如下所示。
    system/core/init/init.cpp

    int main(int argc, char** argv) {
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);
        }
        if (!strcmp(basename(argv[0]), "watchdogd")) {
            return watchdogd_main(argc, argv);
        }
        umask(0);
        add_environment("PATH", _PATH_DEFPATH);
        bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
        //创建文件并挂载
        if (is_first_stage) {
            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));
            mount("sysfs", "/sys", "sysfs", 0, NULL);
        }
        open_devnull_stdio();
        klog_init();
        klog_set_level(KLOG_NOTICE_LEVEL);
        NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
        if (!is_first_stage) {
            // Indicate that booting is in progress to background fw loaders, etc.
            close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
            //初始化属性相关资源
            property_init();//1
            process_kernel_dt();
            process_kernel_cmdline();
            export_kernel_boot_props();
        }
     ...
        //启动属性服务
        start_property_service();//2
        const BuiltinFunctionMap function_map;
        Action::set_function_map(&function_map);
        Parser& parser = Parser::GetInstance();
        parser.AddSectionParser("service",std::make_unique<ServiceParser>());
        parser.AddSectionParser("on", std::make_unique<ActionParser>());
        parser.AddSectionParser("import", std::make_unique<ImportParser>());
        //解析init.rc配置文件
        parser.ParseConfig("/init.rc");//3
       ...   
           while (true) {
            if (!waiting_for_exec) {
                am.ExecuteOneCommand();
                restart_processes();
            }
            int timeout = -1;
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
            if (am.HasMoreCommands()) {
                timeout = 0;
            }
            bootchart_sample(&timeout);
            epoll_event ev;
            int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
            if (nr == -1) {
                ERROR("epoll_wait failed: %s\n", strerror(errno));
            } else if (nr == 1) {
                ((void (*)()) ev.data.ptr)();
            }
        }
        return 0;
    }

    init的main方法做了很多事情,我们只需要关注主要的几点,在注释1处调用 property_init来对属性进行初始化并在注释2处的 调用start_property_service启动属性服务,关于属性服务,后面会讲到。注释3处 parser.ParseConfig(“/init.rc”)用来解析init.rc。解析init.rc的文件为system/core/init/init_parse.cpp文件,接下来我们查看init.rc里做了什么。

    4.init.rc

    init.rc是一个配置文件,内部由Android初始化语言编写(Android Init Language)编写的脚本,它主要包含五种类型语句:
    Action、Commands、Services、Options和Import。init.rc的配置代码如下所示。
    system/core/rootdir/init.rc

    on init
        sysclktz 0
        # Mix device-specific information into the entropy pool
        copy /proc/cmdline /dev/urandom
        copy /default.prop /dev/urandom
    ...
    
    on boot
        # basic network init
        ifup lo
        hostname localhost
        domainname localdomain
        # set RLIMIT_NICE to allow priorities from 19 to -20
        setrlimit 13 40 40
    ...    
    

    这里只截取了一部分代码,其中#是注释符号。on init和on boot是Action类型语句,它的格式为:

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

    为了分析如何创建zygote,我们主要查看Services类型语句,它的格式如下所示:

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

    需要注意的是在Android 7.0中对init.rc文件进行了拆分,每个服务一个rc文件。我们要分析的zygote服务的启动脚本则在init.zygoteXX.rc中定义,这里拿64位处理器为例,init.zygote64.rc的代码如下所示。
    system/core/rootdir/init.zygote64.rc

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
        class main
        socket zygote 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
        writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

    其中service用于通知init进程创建名zygote的进程,这个zygote进程执行程序的路径为/system/bin/app_process64,后面的则是要传给app_process64的参数。class main指的是zygote的class name为main,后文会用到它。

    5.解析service

    接下来我们来解析service,会用到两个函数,一个是ParseSection,它会解析service的rc文件,比如上文讲到的init.zygote64.rc,ParseSection函数主要用来搭建service的架子。另一个是ParseLineSection,用于解析子项。代码如下所示。
    system/core/init/service.cpp

    bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                     std::string* err) {
        if (args.size() < 3) {
            *err = "services must have a name and a program";
            return false;
        }
        const std::string& name = args[1];
        if (!IsValidName(name)) {
            *err = StringPrintf("invalid service name '%s'", name.c_str());
            return false;
        }
        std::vector<std::string> str_args(args.begin() + 2, args.end());
        service_ = std::make_unique<Service>(name, "default", str_args);//1
        return true;
    }
    
    bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                         const std::string& filename, int line,
                                         std::string* err) const {
        return service_ ? service_->HandleLine(args, err) : false;
    }
    

    注释1处,根据参数,构造出一个service对象,它的classname为”default”。当解析完毕时会调用EndSection:

    void ServiceParser::EndSection() {
        if (service_) {
            ServiceManager::GetInstance().AddService(std::move(service_));
        }
    }

    接着查看AddService做了什么:

    void ServiceManager::AddService(std::unique_ptr<Service> service) {
        Service* old_service = FindServiceByName(service->name());
        if (old_service) {
            ERROR("ignored duplicate definition of service '%s'",
                  service->name().c_str());
            return;
        }
        services_.emplace_back(std::move(service));//1
    }

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

    6.init启动zygote

    讲完了解析service,接下来该讲init是如何启动service,在这里我们主要讲解启动zygote这个service。在zygote的启动脚本中我们得知zygote的class name为main。在init.rc有如下配置代码:
    system/core/rootdir/init.rc

    ...
    on nonencrypted    
        # A/B update verifier that marks a successful boot.  
        exec - root -- /system/bin/update_verifier nonencrypted  
        class_start main         
        class_start late_start 
    ...    

    其中class_start是一个COMMAND,对应的函数为do_class_start。我们知道main指的就是zygote,因此class_start main用来启动zygote。do_class_start函数在builtins.cpp中定义,如下所示。

    system/core/init/builtins.cpp

    static int do_class_start(const std::vector<std::string>& args) {
            /* Starting a class does not start services
             * which are explicitly disabled.  They must
             * be started individually.
             */
        ServiceManager::GetInstance().
            ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
        return 0;
    }

    来查看StartIfNotDisabled做了什么:
    system/core/init/service.cpp

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

    接着查看Start方法,如下所示。

    bool Service::Start() {
        flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
        time_started_ = 0;
        if (flags_ & SVC_RUNNING) {//如果Service已经运行,则不启动
            return false;
        }
        bool needs_console = (flags_ & SVC_CONSOLE);
        if (needs_console && !have_console) {
            ERROR("service '%s' requires console\n", name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
      //判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
        struct stat sb;
        if (stat(args_[0].c_str(), &sb) == -1) {
            ERROR("cannot find '%s' (%s), disabling '%s'\n",
                  args_[0].c_str(), strerror(errno), name_.c_str());
            flags_ |= SVC_DISABLED;
            return false;
        }
    
    ...
        pid_t pid = fork();//1.fork函数创建子进程
        if (pid == 0) {//运行在子进程中
            umask(077);
            for (const auto& ei : envvars_) {
                add_environment(ei.name.c_str(), ei.value.c_str());
            }
            for (const auto& si : sockets_) {
                int socket_type = ((si.type == "stream" ? SOCK_STREAM :
                                    (si.type == "dgram" ? SOCK_DGRAM :
                                     SOCK_SEQPACKET)));
                const char* socketcon =
                    !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
    
                int s = create_socket(si.name.c_str(), socket_type, si.perm,
                                      si.uid, si.gid, socketcon);
                if (s >= 0) {
                    PublishSocket(si.name, s);
                }
            }
    ...
            //2.通过execve执行程序
            if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
            }
    
            _exit(127);
        }
    ...
        return true;
    }

    通过注释1和2的代码,我们得知在Start方法中调用fork函数来创建子进程,并在子进程中调用execve执行system/bin/app_process,这样就会进入framework/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);//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.");
            return 10;
        }
    }

    从注释1处的代码可以得知调用runtime(AppRuntime)的start来启动zygote。

    7.属性服务

    Windows平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,它还是能够根据之前在注册表中的记录,进行相应的初始化工作。Android也提供了一个类似的机制,叫做属性服务。
    在本文的开始,我们提到在init.cpp代码中和属性服务相关的代码有:
    system/core/init/init.cpp

      property_init();
      start_property_service();

    这两句代码用来初始化属性服务配置并启动属性服务。首先我们来学习服务配置的初始化和启动。

    属性服务初始化与启动

    property_init函数具体实现的代码如下所示。
    system/core/init/property_service.cpp

    void property_init() {
        if (__system_property_area_init()) {
            ERROR("Failed to initialize property area\n");
            exit(1);
        }
    }

    __system_property_area_init函数用来初始化属性内存区域。接下来查看start_property_service函数的具体代码:

    void start_property_service() {
        property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                        0666, 0, 0, NULL);//1
        if (property_set_fd == -1) {
            ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
            exit(1);
        }
        listen(property_set_fd, 8);//2
        register_epoll_handler(property_set_fd, handle_property_set_fd);//3
    }

    注释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进程将用handle_property_set_fd函数进行处理。
    在linux新的内核中,epoll用来替换select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

    属性服务处理请求
    从上文我们得知,属性服务接收到客户端的请求时,会调用handle_property_set_fd函数进行处理:
    system/core/init/property_service.cpp

    static void handle_property_set_fd()
    {  
    ...
    
            if(memcmp(msg.name,"ctl.",4) == 0) {
                close(s);
                if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
                    handle_control_message((char*) msg.name + 4, (char*) msg.value);
                } else {
                    ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                            msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
                }
            } else {
                //检查客户端进程权限
                if (check_mac_perms(msg.name, source_ctx, &cr)) {//1
                    property_set((char*) msg.name, (char*) msg.value);//2
                } else {
                    ERROR("sys_prop: permission denied uid:%d  name:%s\n",
                          cr.uid, msg.name);
                }
                close(s);
            }
            freecon(source_ctx);
            break;
        default:
            close(s);
            break;
        }
    }

    注释1处的代码用来检查客户端进程权限,在注释2处则调用property_set函数对属性进行修改,代码如下所示。

    int property_set(const char* name, const char* value) {
        int rc = property_set_impl(name, value);
        if (rc == -1) {
            ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
        }
        return rc;
    }

    property_set函数主要调用了property_set_impl函数:

    static int property_set_impl(const char* name, const char* value) {
        size_t namelen = strlen(name);
        size_t valuelen = strlen(value);
        if (!is_legal_property_name(name, namelen)) return -1;
        if (valuelen >= PROP_VALUE_MAX) return -1;
        if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
            if (selinux_reload_policy() != 0) {
                ERROR("Failed to reload policy\n");
            }
        } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
            if (restorecon_recursive(value) != 0) {
                ERROR("Failed to restorecon_recursive %s\n", value);
            }
        }
        //从属性存储空间查找该属性
        prop_info* pi = (prop_info*) __system_property_find(name);
        //如果属性存在
        if(pi != 0) {
           //如果属性以"ro."开头,则表示是只读,不能修改,直接返回
            if(!strncmp(name, "ro.", 3)) return -1;
           //更新属性值
            __system_property_update(pi, value, valuelen);
        } else {
           //如果属性不存在则添加该属性
            int rc = __system_property_add(name, namelen, value, valuelen);
            if (rc < 0) {
                return rc;
            }
        }
        /* If name starts with "net." treat as a DNS property. */
        if (strncmp("net.", name, strlen("net.")) == 0)  {
            if (strcmp("net.change", name) == 0) {
                return 0;
            }
          //以net.开头的属性名称更新后,需要将属性名称写入net.change中  
            property_set("net.change", name);
        } else if (persistent_properties_loaded &&
                strncmp("persist.", name, strlen("persist.")) == 0) {
            /*
             * Don't write properties to disk until after we have read all default properties
             * to prevent them from being overwritten by default values.
             */
            write_persistent_property(name, value);
        }
        property_changed(name, value);
        return 0;
    }

    property_set_impl函数主要用来对属性进行修改,并对以ro、net和persist开头的属性进行相应的处理。到这里,属性服务处理请求的源码就讲到这。

    8.init进程总结

    讲到这,总结起来init进程主要做了三件事:
    1.创建一些文件夹并挂载设备
    2.初始化和启动属性服务
    3.解析init.rc配置文件并启动zygote进程

    参考资料:
    《深入理解Android系统》
    《深入理解Android卷I》
    Android的init过程详解(一)
    Android启动过程深入解析
    Android7.0解析Init.rc文件
    Android 7.0 init.rc的一点改变
    Android7.0 init进程源码分析
    Android情景分析之属性服务


    公众号末尾1.1.jpg

    展开全文
    itachi85 2017-02-07 11:29:48
  • Android系统启动过程往细了说可以分为5步: Loader –》Kernel –》Native –》Framework –》Application Loader Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在ROM里的...

    启动流程

    Android系统启动过程往细了说可以分为5步:
    Loader --》Kernel --》Native --》Framework --》Application

    在这里插入图片描述

    Loader

    • Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在ROM里的预设出代码开始执行,然后加载引导程序到RAM
    • Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能

    Kernel

    Kernel层是指Android内核层,到这里才刚刚开始进入Android系统

    • 启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作
    • 启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。kthreadd进程是所有内核进程的鼻祖

    Native

    这里的Native层主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程,init进程是所有用户进程的鼻祖

    • init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程
    • init进程还启动servicemanager(binder服务管家)、bootanim(开机动画)等重要服务
    • init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程),Zygote是所有Java进程的父进程

    Framework

    • Zygote进程启动后,加载ZygoteInit类,注册Zygote Socket服务端套接字;加载虚拟机;加载类,加载系统资源
    • System Server进程,是由Zygote进程fork而来,System Server是Zygote孵化的第一个进程,System Server负责启动和管理整个Java framework,包含ActivityManager,PowerManager等服务
    • Media Server进程,是由init进程fork而来,负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service等服务

    Application

    • Zygote进程孵化出的第一个App进程是Launcher,即手机桌面APP,没错,手机桌面就是跟我们平时使用的APP一样,它也是一个应用
    • 所有APP进程都是由zygote进程fork出来的

    这些层之间,有的并不能直接交流,比如Native与Kernel之间要经过系统调用才能访问,Java层和Native层需要通过JNI进行调用

    严格来说,Android系统实际上是运行于Linux内核上的一系列服务进程,这些进程是维持设备正常运行的关键,而这些进程的老祖宗就是init进程


    init进程

    上面也介绍到了,当内核启动完成后,就会创建用户空间的第一个进程,即init进程;后面所有的进程,比如Binder机制中的ServiceManager,Zygote都是由init进程孵化出来的

    启动

    当init进程启动后会调用/system/core/init/Init.cpp的main()方法

    int main(int argc, char** argv) {
        ...
        
        klog_init();  //初始化kernel log,位于设备节点/dev/kmsg
        klog_set_level(KLOG_NOTICE_LEVEL); //设置输出的log级别
        // 输出init启动阶段的log
        NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
        
        property_init(); //创建一块共享的内存空间,用于属性服务
        signal_handler_init();  //初始化子进程退出的信号处理过程
    
        property_load_boot_defaults(); //加载default.prop文件
        start_property_service();   //启动属性服务器(通过socket通信)
        init_parse_config_file("/init.rc"); //解析init.rc文件
    
        //执行rc文件中触发器为 on early-init的语句
        action_for_each_trigger("early-init", action_add_queue_tail);
        //等冷插拔设备初始化完成
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        //设备组合键的初始化操作
        queue_builtin_action(keychord_init_action, "keychord_init");
        // 屏幕上显示Android静态Logo 
        queue_builtin_action(console_init_action, "console_init");
        
        //执行rc文件中触发器为 on init的语句
        action_for_each_trigger("init", action_add_queue_tail);
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        
        char bootmode[PROP_VALUE_MAX];
        //当处于充电模式,则charger加入执行队列;否则late-init加入队列。
        if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
           action_for_each_trigger("charger", action_add_queue_tail);
        } else {
           action_for_each_trigger("late-init", action_add_queue_tail);
        }
        //触发器为属性是否设置
        queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
         
        while (true) {
            if (!waiting_for_exec) {
                execute_one_command();
                restart_processes(); 
            }
            int timeout = -1;
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
            if (!action_queue_empty() || cur_action) {
                timeout = 0;
            }
    
            epoll_event ev;
            //循环 等待事件发生
            int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
            if (nr == -1) {
                ERROR("epoll_wait failed: %s\n", strerror(errno));
            } else if (nr == 1) {
                ((void (*)()) ev.data.ptr)();
            }
        }
        return 0;
    }
    

    这里很重要的一句话就是init_parse_config_file,然后去解析init.rc文件,init.rc位于/bootable/recovery/etc/init.rc;需要注意的是这就是一个脚本文件,就像Android打包用到的gradle脚本一样

    rc文件规则

    这个文件解析具体实现在init_parser.cpp文件中,一个完整的init.rc脚本由四种类型的声明组成

    • Action(动作)
    • Commands(命令)
    • Service(服务)
    • Options(选项)

    rc文件有一些通用的语法规则

    • 注释以 # 开头
    • 关键字和参数以空格分割,每个语句以行为单位
    • C语言风格的反斜杠转义字符("")可以用来为参数添加空格
    • 为了防止字符串中的空格把其切割成多个部分,我们需要对其使用双引号
    • 行尾的反斜杠用来表示下面一行是同一行
    • Actions和Service暗示着下一个新语句的开始,这两个关键字后面跟着的commands或者options都属于这个新语句
    • ActionsService有唯一的名字,如果出现和已有Actions和Service重名的会被当做错误而忽略

    Actions

    Actions代表一些Action,Action代表一组命令(Commands),每个Action都有一个trigger(触发器),这个触发器决定了在什么情况下执行该Action中定义的命令;当一些条件满足触发器的条件时,该Action中定义的命令会被添加到“命令执行队列”的尾部,如果命令已经存在了就不会再添加了

    Action的格式如下

    on <trgger> ## on后面接触发条件
       <command1> ## 命令1
       <command2> ## 命令2
       <command3> ## 命令3
       ...
    

    不同的脚本用【on】来区分,on后面跟一个触发器,当被触发时,下面的命令就会以此执行

    常用的有以下几种事件触发器

    类型                      说明
    -------------------------------------------------
    boot                    init.rc被装载后触发
    device-added-<path>     当设备节点添加时触发
    device-removed-<path>   当设备节点移除时触发
    service-exited-<name>   在指定的服务(service)退出时触发
    early-init              init程序初始化之前触发
    late-init               init程序初始化之后触发
    init                    初始化时触发(在 /init.conf (启动配置文件)被装载之后)
    

    Commands

    • exec< path>[< argument>]* :fork并执行一个程序,其路径为< path>,这条命令将阻塞直到该程序启动完成
    • ifup< interface>:使网络接口< interface>成功连接
    • chdir< directory>:更改工作目录< directory>
    • chmod< octal-mode>< path>:更改文件访问权限
    • chroot< directory>:更改根目录位置
    • class_start< serviceclass>:启动由< serviceclass>类名指定的所有相关服务,如果它们不在运行状态的话
    • class_stop< serviceclass>:停止所有由< serviceclass>类名指定的服务,如果它们还在运行的话
    • domainname< name>:设置域名
    • start< service>:启动一个服务,如果没有运行
    • stop< service>:停止一个服务,如果还在运行
    • sysclktz< mins_west_of_gmt>:设置基准时间
    • write< path>< String>[< String>]*:打开一个文件,写入一个或者多个String

    Service

    Service其实是一个可执行程序,以service开头的脚本,在特定选项的约束下会被init程序启动或者重启(Service可以在配置中指定是否需要在退出时重启,这样当Service出现crash时就可以有机会复原)

    脚本格式如下

    service <name> <pathname> [ <argument> ]*
        <option>
        <option>
        ...
    
    • service开头表明这是一个可执行程序
    • < name> 表示服务名
    • < pathname> 此服务所在路径,因为是可执行文件,所以肯定有存储路径
    • < argument> 启动Service所带的参数
    • < option> 对service的设置的选项

    Options

    可用选项如下,也就是上面Service所用到的< option>

    • critical:表明这是对设备至关重要的一个服务,如果它在四分钟内退出超过四次,则设备进入恢复模式
    • disabled:此服务不会自动启动,而是需要显示调用服务名来启动
    • socket< name>< type>< perm>[< user>[< group>] ]:创建一个名为/dev/socket/< name>的unix domain socket,然后将它的fd值传给启动它的进程;有效的< type>值包括dgram,stream,seqpacket,而user和group默认值是0
    • user< username>:在启动服务前将用户切换至< username>,默认情况下用户都是root
    • group< groupname>[< groupname>]*:在启动服务前将用户组切换至< groupname>]
    • oneshot:当该服务退出时,不要主动去重启它
    • class< name>:为该服务指定一个class名,同一个class的所有服务必须同时启动或者停止,默认情况下< name>值是default
    • onrestart:当此服务重启时执行某些命令

    init.rc文件

    # Copyright (C) 2012 The Android Open Source Project
    #
    # IMPORTANT: Do not create world writable files or directories.
    # This is a common source of Android security bugs.
    #
    
    "【import <filename>一个init配置文件,扩展当前配置。】"
    import /init.environ.rc
    import /init.usb.rc
    import /init.${ro.hardware}.rc
    import /init.${ro.zygote}.rc
    import /init.trace.rc
    
    "【触发条件early-init,在early-init阶段调用以下行】"
    on early-init
        # Set init and its forked children's oom_adj.
        write /proc/1/oom_score_adj -1000
        "【打开路径为<path>的一个文件,并写入一个或多个字符串】"
        # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
        write /sys/fs/selinux/checkreqprot 0
    
        # Set the security context for the init process.
        # This should occur before anything else (e.g. ueventd) is started.
        "【这段脚本的意思是init进程启动之后就马上调用函数setcon将自己的安全上下文设置为“u:r:init:s0”,即将init进程的domain指定为init。】"
        setcon u:r:init:s0
    
        # Set the security context of /adb_keys if present.
        "【恢复指定文件到file_contexts配置中指定的安全上线文环境】"
        restorecon /adb_keys
    
        "【执行start ueventd的命令。ueventd是一个service后面有定义】 "
        start ueventd
    
        "【mkdir <path> [mode] [owner] [group]   //创建一个目录<path>,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和root组。】"
        # create mountpoints
        mkdir /mnt 0775 root system
    
    on init
        "【设置系统时钟的基准,比如0代表GMT,即以格林尼治时间为准】"
        sysclktz 0
    
    "【设置kernel日志等级】"
    loglevel 6 ####
        write /proc/bootprof "INIT: on init start" ####
    
        "【symlink <target> <path>    //创建一个指向<path>的软连接<target>。】"
        # Backward compatibility
        symlink /system/etc /etc
        symlink /sys/kernel/debug /d
    
        # Right now vendor lives on the same filesystem as system,
        # but someday that may change.
        symlink /system/vendor /vendor
    
        "【创建一个目录<path>,可以选择性地指定mode、owner以及group。】"
        # Create cgroup mount point for cpu accounting
        mkdir /acct
        mount cgroup none /acct cpuacct
        mkdir /acct/uid
    
        "【mount <type> <device> <dir> [ <mountoption> ]   //在目录<dir>挂载指定的设备。<device> 可以是以 mtd@name 的形式指定一个mtd块设备。<mountoption>包括 ro、rw、remount、noatime、 ...】"
        # Create cgroup mount point for memory
        mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
        mkdir /sys/fs/cgroup/memory 0750 root system
        mount cgroup none /sys/fs/cgroup/memory memory
        write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
        "【chown <owner> <group> <path>   //改变文件的所有者和组。】"
    
        "【后面的一些行因为类似,就省略了】"
        .....
    
    # Healthd can trigger a full boot from charger mode by signaling this
    # property when the power button is held.
    on property:sys.boot_from_charger_mode=1
        "【停止指定类别服务类下的所有已运行的服务】"
        class_stop charger
        "【触发一个事件,将该action排在某个action之后(用于Action排队)】"
        trigger late-init
    
    # Load properties from /system/ + /factory after fs mount.
    on load_all_props_action
        "【从/system,/vendor加载属性。默认包含在init.rc】"
        load_all_props
    
    # Indicate to fw loaders that the relevant mounts are up.
    on firmware_mounts_complete
        "【删除指定路径下的文件】"
        rm /dev/.booting
    
    # Mount filesystems and start core system services.
    on late-init
        "【触发一个事件。用于将一个action与另一个 action排列。】"
        trigger early-fs
        trigger fs
        trigger post-fs
        trigger post-fs-data
    
        # Load properties from /system/ + /factory after fs mount. Place
        # this in another action so that the load will be scheduled after the prior
        # issued fs triggers have completed.
        trigger load_all_props_action
    
        # Remove a file to wake up anything waiting for firmware.
        trigger firmware_mounts_complete
    
        trigger early-boot
        trigger boot
    
    
    on post-fs
        ...
        "【一些创造目录,建立链接,更改权限的操作,这里省略】"
    
    on post-fs-data
        ...
        "【一些创造目录,建立链接,更改权限的操作,这里省略】"
    
        "【恢复指定文件到file_contexts配置中指定的安全上线文环境】"
        restorecon /data/mediaserver
    
        "【将系统属性<name>的值设置为<value>,即以键值对的方式设置系统属性】"
        # Reload policy from /data/security if present.
        setprop selinux.reload_policy 1
    
        "【以递归的方式恢复指定目录到file_contexts配置中指定的安全上下文中】"
        # Set SELinux security contexts on upgrade or policy update.
        restorecon_recursive /data
    
        # If there is no fs-post-data action in the init.<device>.rc file, you
        # must uncomment this line, otherwise encrypted filesystems
        # won't work.
        # Set indication (checked by vold) that we have finished this action
        #setprop vold.post_fs_data_done 1
    
    on boot
        "【初始化网络】"
        # basic network init
        ifup lo
        "【设置主机名为localhost】"
        hostname localhost
        "【设置域名localdomain】"
        domainname localdomain
    
        "【设置资源限制】"
        # set RLIMIT_NICE to allow priorities from 19 to -20
        setrlimit 13 40 40
    
        "【这里省略了一些chmod,chown,等操作,不多解释】"
       ...
    
    
        # Define default initial receive window size in segments.
        setprop net.tcp.default_init_rwnd 60
    
        "【重启core服务】"
        class_start core
    
    on nonencrypted
        class_start main
        class_start late_start
    
    on property:vold.decrypt=trigger_default_encryption
        start defaultcrypto
    
    on property:vold.decrypt=trigger_encryption
        start surfaceflinger
        start encrypt
    
    on property:sys.init_log_level=*
        loglevel ${sys.init_log_level}
    
    on charger
        class_start charger
    
    on property:vold.decrypt=trigger_reset_main
        class_reset main
    
    on property:vold.decrypt=trigger_load_persist_props
        load_persist_props
    
    on property:vold.decrypt=trigger_post_fs_data
        trigger post-fs-data
    
    on property:vold.decrypt=trigger_restart_min_framework
        class_start main
    
    on property:vold.decrypt=trigger_restart_framework
        class_start main
        class_start late_start
    
    on property:vold.decrypt=trigger_shutdown_framework
        class_reset late_start
        class_reset main
    
    on property:sys.powerctl=*
        powerctl ${sys.powerctl}
    
    # system server cannot write to /proc/sys files,
    # and chown/chmod does not work for /proc/sys/ entries.
    # So proxy writes through init.
    on property:sys.sysctl.extra_free_kbytes=*
        write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
    
    # "tcp_default_init_rwnd" Is too long!
    on property:sys.sysctl.tcp_def_init_rwnd=*
        write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
    
    "【守护进程】"
    ## Daemon processes to be run by init.
    ##
    service ueventd /sbin/ueventd
        class core
        critical
        seclabel u:r:ueventd:s0
    
    "【日志服务进程】"
    service logd /system/bin/logd
        class core
        socket logd stream 0666 logd logd
        socket logdr seqpacket 0666 logd logd
        socket logdw dgram 0222 logd logd
        seclabel u:r:logd:s0
    
    "【Healthd是android4.4之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给Framework层的BatteryService用以计算电池电量相关状态信息】"
    service healthd /sbin/healthd
        class core
        critical
        seclabel u:r:healthd:s0
    
    "【控制台进程】"
    service console /system/bin/sh
        "【为当前service设定一个类别.相同类别的服务将会同时启动或者停止,默认类名是default】"
        class core
        "【服务需要一个控制台】"
        console
        "【服务不会自动启动,必须通过服务名显式启动】"
        disabled
        "【在执行此服务之前切换用户名,当前默认的是root.自Android M开始,即使它要求linux capabilities,也应该使用该选项.很明显,为了获得该功能,进程需要以root用户运行】"
        user shell
        seclabel u:r:shell:s0
    
    on property:ro.debuggable=1
        start console
    
    # 启动adbd服务进程
    service adbd /sbin/adbd --root_seclabel=u:r:su:s0
        class core
        "【创建一个unix域下的socket,其被命名/dev/socket/<name>. 并将其文件描述符fd返回给服务进程.其中,type必须为dgram,stream或者seqpacke,user和group默认是0.seclabel是该socket的SELLinux的安全上下文环境,默认是当前service的上下文环境,通过seclabel指定】"
        socket adbd stream 660 system system
        disabled
        seclabel u:r:adbd:s0
    
    # adbd on at boot in emulator
    on property:ro.kernel.qemu=1
        start adbd
    
    "【内存管理服务,内存不够释放内存】"
    service lmkd /system/bin/lmkd
        class core
        critical
        socket lmkd seqpacket 0660 system system
    
    "【ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信。
    在Android系统中用到最多的通信机制就是Binder,Binder主要由Client、Server、ServiceManager和Binder驱动程序组成。其中Client、Service和ServiceManager运行在用户空间,而Binder驱动程序运行在内核空间。核心组件就是Binder驱动程序了,而ServiceManager提供辅助管理的功能,无论是Client还是Service进行通信前首先要和ServiceManager取得联系。而ServiceManager是一个守护进程,负责管理Server并向Client提供查询Server的功能。】"
    service servicemanager /system/bin/servicemanager
        class core
        user system
        group system
        critical
        onrestart restart healthd
        "【servicemanager 服务启动时会重启zygote服务】"
        onrestart restart zygote
        onrestart restart media
        onrestart restart surfaceflinger
        onrestart restart drm
    
    "【Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程】"
    service vold /system/bin/vold
        class core
        socket vold stream 0660 root mount
        ioprio be 2
    
    "【Netd是Android系统中专门负责网络管理和控制的后台daemon程序】"
    service netd /system/bin/netd
        class main
        socket netd stream 0660 root system
        socket dnsproxyd stream 0660 root inet
        socket mdns stream 0660 root system
        socket fwmarkd stream 0660 root inet
    
    "【debuggerd是一个daemon进程,在系统启动时随着init进程启动。主要负责将进程运行时的信息dump到文件或者控制台中】"
    service debuggerd /system/bin/debuggerd
        class main
    
    service debuggerd64 /system/bin/debuggerd64
        class main
    
    "【Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层】"
    # for using TK init.modem.rc rild-daemon setting
    #service ril-daemon /system/bin/rild
    #    class main
    #    socket rild stream 660 root radio
    #    socket rild-debug stream 660 radio system
    #    user root
    #    group radio cache inet misc audio log
    
    "【提供系统 范围内的surface composer功能,它能够将各种应用 程序的2D、3D surface进行组合。】"
    service surfaceflinger /system/bin/surfaceflinger
        class core
        user system
        group graphics drmrpc
        onrestart restart zygote
    
    "【DRM可以直接访问DRM clients的硬件。DRM驱动用来处理DMA,内存管理,资源锁以及安全硬件访问。为了同时支持多个3D应用,3D图形卡硬件必须作为一个共享资源,因此需要锁来提供互斥访问。DMA传输和AGP接口用来发送图形操作的buffers到显卡硬件,因此要防止客户端越权访问显卡硬件。】"
    #make sure drm server has rights to read and write sdcard ####
    service drm /system/bin/drmserver
        class main
        user drm
        # group drm system inet drmrpc ####
        group drm system inet drmrpc sdcard_r ####
    
    "【媒体服务,无需多说】"
    service media /system/bin/mediaserver
        class main
        user root ####
    #   google default ####
    #   user media    ####
        group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm media sdcard_r system net_bt_stack ####
    #   google default ####
    #   group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ####
    
        ioprio rt 4
    
    "【设备加密相关服务】"
    # One shot invocation to deal with encrypted volume.
    service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
        disabled
        "【当服务退出时,不重启该服务】"
        oneshot
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption) or trigger_restart_min_framework (other encryption)
    
    # One shot invocation to encrypt unencrypted volumes
    service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
        disabled
        oneshot
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption)
    
    "【开机动画服务】"
    service bootanim /system/bin/bootanimation
        class core
        user graphics
    #    group graphics audio ####
        group graphics media audio ####
        disabled
        oneshot
    
    "【在Android系统中,PackageManagerService用于管理系统中的所有安装包信息及应用程序的安装卸载,但是应用程序的安装与卸载并非PackageManagerService来完成,而是通过PackageManagerService来访问installd服务来执行程序包的安装与卸载的。】"
    service installd /system/bin/installd
        class main
        socket installd stream 600 system system
    
    service flash_recovery /system/bin/install-recovery.sh
        class main
        seclabel u:r:install_recovery:s0
        oneshot
    
    "【vpn相关的服务】"
    service racoon /system/bin/racoon
        class main
        socket racoon stream 600 system system
        # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
        group vpn net_admin inet
        disabled
        oneshot
    
    "【android中有mtpd命令可以连接vpn】"
    service mtpd /system/bin/mtpd
        class main
        socket mtpd stream 600 system system
        user vpn
        group vpn net_admin inet net_raw
        disabled
        oneshot
    
    service keystore /system/bin/keystore /data/misc/keystore
        class main
        user keystore
        group keystore drmrpc
    
    "【可以用dumpstate 获取设备的各种信息】"
    service dumpstate /system/bin/dumpstate -s
        class main
        socket dumpstate stream 0660 shell log
        disabled
        oneshot
    
    "【mdnsd 是多播 DNS 和 DNS 服务发现的守护程序。】"
    service mdnsd /system/bin/mdnsd
        class main
        user mdnsr
        group inet net_raw
        socket mdnsd stream 0660 mdnsr inet
        disabled
        oneshot
    
    "【触发关机流程继续往下走】"
    service pre-recovery /system/bin/uncrypt
        class main
        disabled
        "【当服务退出时,不重启该服务】"
        oneshot
    

    启动顺序是on early-init -> init -> late-init -> boot,接下来就是各种服务的启动

    init进程的功能

    • 分析和运行所有的init.rc文件
    • 生成设备驱动节点(通过rc文件创建)
    • 处理子进程的终止(signal方式)
    • 提供属性服务

    Zygote进程

    在Android中,zygote是整个系统创建新进程的核心进程。在init进程启动后就会创建zygote进程;zygote进程在内部会先启动Dalvik虚拟机,继而加载一些必要的系统资源和系统类,最后进入一种监听状态。在之后的运作中,当其他系统模块(比如AMS)希望创建新进程时,只需向zygote进程发出请求,zygote进程监听到该请求后,会相应地fork出新的进程,于是这个新进程在初生之时,就先天具有了自己的Dalvik虚拟机以及系统资源

    其实在早期的Android版本中,Zygote的启动命令直接是写在init.rc中的,但是随着硬件的不断升级换代,Android系统也要面对32位和64位机器同时存在的情况,所以对Zygote启动也需要根据不同情况对待

    在init.rc顶部可以看到有这么一句话

    import /init.${ro.zygote}.rc

    这里会根据系统属性ro.zygote的值去加载不同的描述Zygote的rc脚本,比如

    • init.zygote32.rc
    • init.zygote64.rc
    • init.zygote32_64.rc
    • init.zygote64_32.rc

    以init.zygote64_32.rc为例

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
        class main
        socket zygote 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
        writepid /dev/cpuset/foreground/tasks
     
    service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
        class main
        socket zygote_secondary stream 660 root system
        onrestart restart zygote
        writepid /dev/cpuset/foreground/tasks
    

    如上,可以看到服务名(进程名)是zygote,对应的可执行程序是app_processXX,而且还创建了一个名为zygote的unix domain socket,类型是stream,这个socket是为了后面IPC所用;上面可以看到还有一个zygote_secondary的进程,其实这是为了适配不同的abi型号

    其中Zygote进程能够重启的地方有

    • servicemanager进程被杀
    • surfaceflinger进程被杀
    • Zygote进程自己被杀
    • system_server进程被杀

    接下来看看zygote启动过程,zygote对应的可执行文件就是/system/bin/app_processXX,也就是说系统启动时会执行到这个可执行文件的main()函数里

    main

    int main(int argc, char* const argv[])
    {
        //Android运行时环境,传到的参数argv为“-Xzygote /system/bin --zygote --start-system-server”
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
        argc--; argv++; //忽略第一个参数
    
        int i;
        for (i = 0; i < argc; i++) {
            if (argv[i][0] != '-') {
                break;
            }
            if (argv[i][1] == '-' && argv[i][2] == 0) {
                ++i;
                break;
            }
            runtime.addOption(strdup(argv[i]));
        }
        //参数解析
        bool zygote = false;
        bool startSystemServer = false;
        bool application = false;
        String8 niceName;
        String8 className;
        ++i;
        while (i < argc) {
            const char* arg = argv[i++];
            if (strcmp(arg, "--zygote") == 0) {
                zygote = true;
                //--zygote表示当前进程用于承载zygote
                //对于64位系统nice_name为zygote64; 32位系统为zygote
                niceName = ZYGOTE_NICE_NAME;
            } else if (strcmp(arg, "--start-system-server") == 0) {
    	        //是否需要启动system server
                startSystemServer = true;
            } else if (strcmp(arg, "--application") == 0) {
    	        //启动进入独立的程序模式
                application = true;
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
    	        //niceName 为当前进程别名,区别abi型号
                niceName.setTo(arg + 12);
            } else if (strncmp(arg, "--", 2) != 0) {
                className.setTo(arg);
                break;
            } else {
                --i;
                break;
            }
        }
        Vector<String8> args;
        if (!className.isEmpty()) {
            // 运行application或tool程序
            args.add(application ? String8("application") : String8("tool"));
            runtime.setClassNameAndArgs(className, argc - i, argv + i);
        } else {
            //进入zygote模式,创建 /data/dalvik-cache路径
            maybeCreateDalvikCache();
            if (startSystemServer) {
                args.add(String8("start-system-server"));
            }
            char prop[PROP_VALUE_MAX];
            if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
                return 11;
            }
            String8 abiFlag("--abi-list=");
            abiFlag.append(prop);
            args.add(abiFlag);
    
            for (; i < argc; ++i) {
                args.add(String8(argv[i]));
            }
        }
    
        //设置进程名
        if (!niceName.isEmpty()) {
            runtime.setArgv0(niceName.string());
            set_process_name(niceName.string());
        }
        if (zygote) {
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        } else if (className) {
            runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } else {
            //没有指定类名或zygote,参数错误
            return 10;
        }
    }
    
    

    根据传入参数的不同可以有两种启动方式,一个是 “com.android.internal.os.RuntimeInit”, 另一个是 ”com.android.internal.os.ZygoteInit", 对应RuntimeInit 和 ZygoteInit 两个类, 这两个类的主要区别在于Java端,可以明显看出,ZygoteInit 相比 RuntimeInit 多做了很多事情,比如说 “preload", “gc” 等等。但是在Native端,他们都做了相同的事, startVM() 和 startReg()

    在当前场景中,init.rc指定了–zygote选项,并且args有添加start-system-server值,所以接下来执行

    AndroidRuntime::start

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        static const String8 startSystemServer("start-system-server");
    
        for (size_t i = 0; i < options.size(); ++i) {
            if (options[i] == startSystemServer) {
               const int LOG_BOOT_PROGRESS_START = 3000;
            }
        }
        const char* rootDir = getenv("ANDROID_ROOT");
        if (rootDir == NULL) {
            rootDir = "/system";
            if (!hasDir("/system")) {
                return;
            }
            setenv("ANDROID_ROOT", rootDir, 1);
        }
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);
        JNIEnv* env;
        // 虚拟机创建,主要篇幅是关于虚拟机参数的设置
        if (startVm(&mJavaVM, &env, zygote) != 0) {
            return;
        }
        onVmCreated(env);
        // JNI方法注册
        if (startReg(env) < 0) {
            return;
        }
    
        jclass stringClass;
        jobjectArray strArray;
        jstring classNameStr;
    
        //等价 strArray= new String[options.size() + 1];
        stringClass = env->FindClass("java/lang/String");
        strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    
        //等价 strArray[0] = "com.android.internal.os.ZygoteInit"
        classNameStr = env->NewStringUTF(className);
        env->SetObjectArrayElement(strArray, 0, classNameStr);
    
        //等价 strArray[1] = "start-system-server";
        //    strArray[2] = "--abi-list=xxx";
        //其中xxx为系统响应的cpu架构类型,比如arm64-v8a.
        for (size_t i = 0; i < options.size(); ++i) {
            jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
            env->SetObjectArrayElement(strArray, i + 1, optionsStr);
        }
    
        //将"com.android.internal.os.ZygoteInit"转换为"com/android/internal/os/ZygoteInit"
        char* slashClassName = toSlashClassName(className);
        //找到Zygoteinit类
        jclass startClass = env->FindClass(slashClassName);
        if (startClass == NULL) {
            ...
        } else {
        //找到这个类后就继续找成员函数main方法的Mehtod ID
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
            // 通过反射调用ZygoteInit.main()方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
        //释放相应对象的内存空间
        free(slashClassName);
        mJavaVM->DetachCurrentThread();
        mJavaVM->DestroyJavaVM();
    }
    

    AndroidRuntime::startVm

    int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
    {
        // JNI检测功能,用于native层调用jni函数时进行常规检测,比较弱字符串格式是否符合要求,资源是否正确释放。该功能一般用于早期系统调试或手机Eng版,对于User版往往不会开启,引用该功能比较消耗系统CPU资源,降低系统性能。
        bool checkJni = false;
        property_get("dalvik.vm.checkjni", propBuf, "");
        if (strcmp(propBuf, "true") == 0) {
            checkJni = true;
        } else if (strcmp(propBuf, "false") != 0) {
            property_get("ro.kernel.android.checkjni", propBuf, "");
            if (propBuf[0] == '1') {
                checkJni = true;
            }
        }
        if (checkJni) {
            addOption("-Xcheck:jni");
        }
    
        //虚拟机产生的trace文件,主要用于分析系统问题,路径默认为/data/anr/traces.txt
        parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");
    
        //对于不同的软硬件环境,这些参数往往需要调整、优化,从而使系统达到最佳性能
        parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
        parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
        parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
        parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
        parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
        parseRuntimeOption("dalvik.vm.heaptargetutilization",
                           heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
        ...
    
        //preloaded-classes文件内容是由WritePreloadedClassFile.java生成的,
        //在ZygoteInit类中会预加载工作将其中的classes提前加载到内存,以提高系统性能
        if (!hasFile("/system/etc/preloaded-classes")) {
            return -1;
        }
    
        //初始化虚拟机
        if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
            ALOGE("JNI_CreateJavaVM failed\n");
            return -1;
        }
    }
    

    AndroidRuntime::startReg

    int AndroidRuntime::startReg(JNIEnv* env)
    {
        //设置线程创建方法为javaCreateThreadEtc 
        androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
    
        env->PushLocalFrame(200);
        //进程JNI方法的注册
        if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
            env->PopLocalFrame(NULL);
            return -1;
        }
        env->PopLocalFrame(NULL);
        return 0;
    }
    

    总结一下Zygote native 进程做了哪些主要工作:

    • 创建虚拟机–startVM
    • 注册JNI函数–startReg
    • 通过JNI知道Java层的com.android.internal.os.ZygoteInit 类,调用main 函数,进入java 世界

    这就开始进入java层了

    /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    ZygoteInit.main

    public static void main(String argv[]) {
        try {
            RuntimeInit.enableDdms(); //开启DDMS功能
            SamplingProfilerIntegration.start();
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }
            ...
    
            registerZygoteSocket(socketName); //为Zygote注册socket
            preload(); // 预加载类和资源
            SamplingProfilerIntegration.writeZygoteSnapshot();
            gcAndFinalize(); //GC操作
            if (startSystemServer) {
                startSystemServer(abiList, socketName);//启动system_server
            }
            runSelectLoop(abiList); //进入循环模式
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run(); //启动system_server中会讲到。
        } catch (RuntimeException ex) {
            closeServerSocket();
            throw ex;
        }
    }
    
    

    ZygoteInit.registerZygoteSocket

    private static void registerZygoteSocket(String socketName) {
        if (sServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                ...
            }
    
            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc); //设置文件描述符
                sServerSocket = new LocalServerSocket(fd); //创建Socket的本地服务端
            } catch (IOException ex) {
                ...
            }
        }
    }
    

    在这里就是实例化一个LocalServerSocket,这样zygote就可以作为服务端,不断的获取其它进程发送过来的请求

    ZygoteInit.preload

    static void preload() {
        //预加载位于/system/etc/preloaded-classes文件中的类
        preloadClasses();
    
        //预加载资源,包含drawable和color资源
        preloadResources();
    
        //预加载OpenGL
        preloadOpenGL();
    
        //通过System.loadLibrary()方法,
        //预加载"android","compiler_rt","jnigraphics"这3个共享库
        preloadSharedLibraries();
    
        //预加载  文本连接符资源
        preloadTextResources();
    
        //仅用于zygote进程,用于内存共享的进程
        WebViewFactory.prepareWebViewInZygote();
    }
    

    执行Zygote进程的初始化,对于类加载,采用反射机制Class.forName()方法来加载。对于资源加载,主要是 com.android.internal.R.array.preloaded_drawables和com.android.internal.R.array.preloaded_color_state_lists,在应用程序中以com.android.internal.R.xxx开头的资源,便是此时由Zygote加载到内存的

    ZygoteInit.runSelectLoop

    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        //sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]
        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);
    
        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                 //处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                ...
            }
            
            for (int i = pollFds.length - 1; i >= 0; --i) {
                //采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;
                // 否则进入continue,跳出本次循环。
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                if (i == 0) {
                    //即fds[0],代表的是sServerSocket,则意味着有客户端连接请求;
                    // 则创建ZygoteConnection对象,并添加到fds。
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor()); //添加到fds.
                } else {
                    //i>0,则代表通过socket接收来自对端的数据,并执行相应操作
                    boolean done = peers.get(i).runOnce();
                    if (done) {
                        peers.remove(i);
                        fds.remove(i); //处理完则从fds中移除该文件描述符
                    }
                }
            }
        }
    }
    
    

    ZygoteInit.acceptCommandPeer

    private static ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return new ZygoteConnection(sServerSocket.accept(), abiList);
        } catch (IOException ex) {
            ...
        }
    }
    

    ZygoteConnection.runOnce

    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;
    
        try {
            //读取socket客户端发送过来的参数列表
            args = readArgumentList();
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            ...
            return true;
        }
        ...
    
        try {
            //将binder客户端传递过来的参数,解析成Arguments对象格式
            parsedArgs = new Arguments(args);
            ...
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);
        } catch (Exception e) {
            ...
        }
    
        try {
            if (pid == 0) {
                //子进程执行
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                //进入子进程流程
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
                return true;
            } else {
                //父进程执行
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }
    

    接收客户端发送过来的connect()操作,Zygote作为服务端执行accept()操作。 再后面客户端调用write()写数据,Zygote进程调用read()读数据。

    没有连接请求时会进入休眠状态,当有创建新进程的连接请求时,唤醒Zygote进程,创建Socket通道ZygoteConnection,然后执行ZygoteConnection的runOnce()方法。

    Zygote总结:

    • 解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()方法
    • 调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
    • 通过JNI方式调用ZygoteInit.main(),第一次进入Java世界
    • registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应客户端请求
    • preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
    • 通过startSystemServer(),fork得力帮手system_server进程,也是Java Framework的运行载体(下面讲到system server再详细讲解)
    • 调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作

    System Server进程

    system server进程和zygote进程可以说是Android世界中的两大最重要的进程,离开其中之一基本上系统就玩完了;基本上在Java Framework中的大多数服务都是在system server进程中一个线程的方式存在的,如下:

    • EntropyService 提供伪随机数
    • PowerManagerService 电源管理服务
    • ActivityManagerService 最核心的服务之一,管理 四大组件
    • TelephonyRegistry 通过该服务注册电话模块的事件响应,比如重启、关闭、启动等
    • PackageManagerService 程序包管理服务
    • AccountManagerService 账户管理服务,是指联系人账户,而不是 Linux 系统的账户
    • ContentService ContentProvider 服务,提供跨进程数据交换
    • BatteryService 电池管理服务
    • LightsService 自然光强度感应传感器服务
    • VibratorService 震动器服务
    • AlarmManagerService 定时器管理服务,提供定时提醒服务
    • WindowManagerService Framework 最核心的服务之一,负责窗口管理
    • BluetoothService 蓝牙服务
    • DevicePolicyManagerService 提供一些系统级别的设置及属性
    • StatusBarManagerService 状态栏管理服务
    • ClipboardService 系统剪切板服务
    • InputMethodManagerService 输入法管理服务
    • NetStatService 网络状态服务
    • NetworkManagementService 网络管理服务
    • ConnectivityService 网络连接管理服务
    • ThrottleService 暂不清楚其作用
    • AccessibilityManagerService 辅助管理程序截获所有的用户输入,并根据这些输入给用户一些额外的反馈,起到辅助的效果
    • MountService 挂载服务,可通过该服务调用 Linux 层面的 mount 程序
    • NotificationManagerService 通知栏管理服务, Android 中的通知栏和状态栏在一起,只是界面上前者在左边,后者在右边
    • DeviceStorageMonitorService 磁盘空间状态检测服务
    • LocationManagerService 地理位置服务
    • SearchManagerService 搜索管理服务
    • DropBoxManagerService 通过该服务访问 Linux 层面的 Dropbox 程序
    • WallpaperManagerService 墙纸管理服务,墙纸不等同于桌面背景,在 View 系统内部,墙纸可以作为任何窗口的背景
    • AudioService 音频管理服务
    • BackupManagerService 系统备份服务
    • AppWidgetService Widget 服务
    • RecognitionManagerService 身份识别服务
    • DiskStatsService 磁盘统计服务

    system server进程也是由zygote进程fork出来的,在上面的ZygoteInit.main方法中有如下代码

    ZygoteInit.main

    public static void main(String argv[]) {
        try {
            if (startSystemServer) {
                startSystemServer(abiList, socketName);//启动system_server
            }
        } catch (MethodAndArgsCaller caller) {
            caller.run(); //这一步很重要,接下来会讲到
        } catch (RuntimeException ex) {
            closeServerSocket();
            throw ex;
        }
    }
    

    ZygoteInit.startSystemServer

    private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
        ...
        //参数准备
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
    
        ZygoteConnection.Arguments parsedArgs = null;
        int pid;
        try {
            //用于解析参数,生成目标格式
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
    
            // fork子进程,该进程是system_server进程
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
    
        //进入子进程system_server
        if (pid == 0) {
    	    //第二个zygote进程
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            // 完成system_server进程剩余的工作 
            handleSystemServerProcess(parsedArgs);
        }
        return true;
    }
    
    

    这个方法先准备参数,然后fork新进程,对于有两个zygote进程情况,需等待第2个zygote创建完成

    Zygote.forkSystemServer

    public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        VM_HOOKS.preFork();
        // 调用native方法fork system_server进程
        int pid = nativeForkSystemServer(
                uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
        if (pid == 0) {
            Trace.setTracingEnabled(true);
        }
        VM_HOOKS.postForkCommon();
        return pid;
    }
    

    nativeForkSystemServer()方法在AndroidRuntime.cpp中注册的,调用com_android_internal_os_Zygote.cpp中的register_com_android_internal_os_Zygote()方法建立native方法的映射关系,所以接下来进入如下方法

    com_android_internal_os_Zygote.nativeForkSystemServer

    static jint com_android_internal_os_Zygote_nativeForkSystemServer(
            JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
            jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
            jlong effectiveCapabilities) {
      //fork子进程
      pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
                                          debug_flags, rlimits,
                                          permittedCapabilities, effectiveCapabilities,
                                          MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
                                          NULL, NULL);
      if (pid > 0) {
          // zygote进程,检测system_server进程是否创建
          gSystemServerPid = pid;
          int status;
          if (waitpid(pid, &status, WNOHANG) == pid) {
              //当system_server进程死亡后,重启zygote进程
              RuntimeAbort(env);
          }
      }
      return pid;
    }
    

    当system_server进程创建失败时,将会重启zygote进程。这里需要注意,对于Android 5.0以上系统,有两个zygote进程,分别是zygote、zygote64两个进程,system_server的父进程,一般来说64位系统其父进程是zygote64进程

    • 当kill system_server进程后,只重启zygote64和system_server,不重启zygote;
    • 当kill zygote64进程后,只重启zygote64和system_server,也不重启zygote;
    • 当kill zygote进程,则重启zygote、zygote64以及system_server。

    com_android_internal_os_Zygote.ForkAndSpecializeCommon

    static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                         jint debug_flags, jobjectArray javaRlimits,
                                         jlong permittedCapabilities, jlong effectiveCapabilities,
                                         jint mount_external,
                                         jstring java_se_info, jstring java_se_name,
                                         bool is_system_server, jintArray fdsToClose,
                                         jstring instructionSet, jstring dataDir) {
      SetSigChldHandler(); //设置子进程的signal信号处理函数
      pid_t pid = fork(); //fork子进程
      if (pid == 0) {
        //进入子进程
        DetachDescriptors(env, fdsToClose); //关闭并清除文件描述符
    
        if (!is_system_server) {
            //对于非system_server子进程,则创建进程组
            int rc = createProcessGroup(uid, getpid());
        }
        SetGids(env, javaGids); //设置设置group
        SetRLimits(env, javaRlimits); //设置资源limit
    
        int rc = setresgid(gid, gid, gid);
        rc = setresuid(uid, uid, uid);
    
        SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
        SetSchedulerPolicy(env); //设置调度策略
    
         //selinux上下文
        rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
    
        if (se_info_c_str == NULL && is_system_server) {
          se_name_c_str = "system_server";
        }
        if (se_info_c_str != NULL) {
          SetThreadName(se_name_c_str); //设置线程名为system_server,方便调试
        }
        UnsetSigChldHandler(); //设置子进程的signal信号处理函数为默认函数
        //等价于调用zygote.callPostForkChildHooks()
        env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
                                  is_system_server ? NULL : instructionSet);
        ...
    
      } else if (pid > 0) {
        //进入父进程,即zygote进程
      }
      return pid;
    }
    
    int fork() {
      __bionic_atfork_run_prepare(); 
    
      pthread_internal_t* self = __get_thread();
    
      //fork期间,获取父进程pid,并使其缓存值无效
      pid_t parent_pid = self->invalidate_cached_pid();
      //系统调用
      int result = syscall(__NR_clone, FORK_FLAGS, NULL, NULL, NULL, &(self->tid));
      if (result == 0) {
        self->set_cached_pid(gettid());
        __bionic_atfork_run_child(); //fork完成执行子进程回调方法
      } else {
        self->set_cached_pid(parent_pid);
        __bionic_atfork_run_parent(); //fork完成执行父进程回调方法
      }
      return result;
    }
    

    fork

    fork()采用copy on write技术,这是linux创建进程的标准方法,调用一次,返回两次,返回值有3种类型

    • 父进程中,fork返回新创建的子进程的pid;
    • 子进程中,fork返回0;
    • 当出现错误时,fork返回负数。(当进程数超过上限或者系统内存不足时会出错)

    fork()的主要工作是寻找空闲的进程号pid,然后从父进程拷贝进程信息,例如数据段和代码段,fork()后子进程要执行的代码等。 Zygote进程是所有Android进程的母体,包括system_server和各个App进程。zygote利用fork()方法生成新进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A
    在这里插入图片描述

    fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
    可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们就开始分别作不同的工作,正如fork原意【分支】一样

    到此system_server进程已完成了创建的所有工作,接下来开始了system_server进程的真正工作。在前面startSystemServer()方法中,zygote进程执行完forkSystemServer()后,新创建出来的system_server进程便进入handleSystemServerProcess()方法

    ZygoteInit.handleSystemServerProcess

    private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {
    
        closeServerSocket(); //关闭父进程zygote复制而来的Socket
    
        Os.umask(S_IRWXG | S_IRWXO);
    
        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName); //设置当前进程名为"system_server"
        }
    
        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        if (systemServerClasspath != null) {
            //执行dex优化操作
            performSystemServerDexOpt(systemServerClasspath);
        }
    
        if (parsedArgs.invokeWith != null) {
            String[] args = parsedArgs.remainingArgs;
    
            if (systemServerClasspath != null) {
                String[] amendedArgs = new String[args.length + 2];
                amendedArgs[0] = "-cp";
                amendedArgs[1] = systemServerClasspath;
                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
            }
            //启动应用进程
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(), null, args);
        } else {
            ClassLoader cl = null;
            if (systemServerClasspath != null) {
                创建类加载器,并赋予当前线程
                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
                Thread.currentThread().setContextClassLoader(cl);
            }
    
            //system_server故进入此分支
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
        }
    }
    
    

    RuntimeInit.zygoteInit

    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
    
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams(); //重定向log输出
    
        commonInit(); // 通用的一些初始化
        nativeZygoteInit(); // zygote初始化 
        applicationInit(targetSdkVersion, argv, classLoader); // 应用初始化
    }
    
    private static final void commonInit() {
        // 设置默认的未捕捉异常处理方法
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
    
        // 设置市区,中国时区为"Asia/Shanghai"
        TimezoneGetter.setInstance(new TimezoneGetter() {
            @Override
            public String getId() {
                return SystemProperties.get("persist.sys.timezone");
            }
        });
        TimeZone.setDefault(null);
    
        //重置log配置
        LogManager.getLogManager().reset();
        new AndroidConfig();
    
        // 设置默认的HTTP User-agent格式( "Dalvik/1.1.0 (Linux; U; Android 6.0.1;LenovoX3c70 Build/LMY47V)"),用于 HttpURLConnection
        String userAgent = getDefaultUserAgent();
        System.setProperty("http.agent", userAgent);
    
        // 设置socket的tag,用于网络流量统计
        NetworkManagementSocketTagger.install();
    }
    
    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        //true代表应用程序退出时不调用AppRuntime.onExit(),否则会在退出前调用
        nativeSetExitWithoutCleanup(true);
    
        //设置虚拟机的内存利用率参数值为0.75
        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
    
        final Arguments args;
        try {
            args = new Arguments(argv); //解析参数
        } catch (IllegalArgumentException ex) {
            return;
        }
    
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    
        //调用startClass的static方法 main() 此处args.startClass为”com.android.server.SystemServer”
        invokeStaticMain(args.startClass, args.startArgs, classLoader);
    }
    
    private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl = Class.forName(className, true, classLoader);
        ...
    
        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            ...
        } catch (SecurityException ex) {
            ...
        }
    
        //通过抛出异常,回到ZygoteInit.main()。这样做好处是能清空栈帧,提高栈帧利用率
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }
    
    

    重点看最后一个方法,通过反射获取SystemServer类的main方法参数,然后抛出MethodAndArgsCaller异常;但是抛出异常后怎么弄呢,我们知道一个方法抛异常,会一直走到调用方法,直到一个方法捕获了异常,这里就是开头讲的,在ZygoteInit.main方法捕获了异常然后去执行

    ZygoteInit.main

    public static void main(String argv[]) {
        try {
            if (startSystemServer) {
                startSystemServer(abiList, socketName);//启动system_server
            }
        } catch (MethodAndArgsCaller caller) {
            caller.run(); //这一步很重要,接下来会讲到
        } catch (RuntimeException ex) {
            closeServerSocket();
            throw ex;
        }
    }
    

    但是为啥没有直接在startSystemServer()或者上面的方法中直接调用SystemServer类的main方法,而是通过抛异常的方式处理呢?

    我们知道,当一个函数抛出异常后,这个异常会依次传递给调用它的函数,直到这个异常被捕获,如果这个异常一直没有被处理,最终就会引起程序的崩溃。

    程序都是由一个个函数组成的(除了汇编程序),c/c++/java/…等高级语言编写的应用程序,在执行的时候,他们都拥有自己的栈空间(是一种先进后出的内存区域),用于存放函数的返回地址和函数的临时数据,每调用一个函数时,就会把函数的返回地址和相关数据压入栈中,当一个函数执行完后,就会从栈中弹出,cpu会根据函数的返回地址,执行上一个调用函数的下一条指令。

    所以,在抛出异常后,如果异常没有在当前的函数中捕获,那么当前的函数执行就会异常的退出,从应用程序的栈弹出,并将这个异常传递给上一个函数,直到异常被捕获处理,否则,就会引起程序的崩溃。

    因此,这里通过抛异常的方式启动主要是清理应用程序栈中ZygoteInit.main以上的函数栈帧,以实现当相应的main函数退出时,能直接退出整个应用程序

    ZygoteInit.MethodAndArgsCaller.run

    public static class MethodAndArgsCaller extends Exception
                implements Runnable {
            /** 调用的方法 在上面的方法可知是main方法 */
            private final Method mMethod;
    
            /** 参数列表 */
            private final String[] mArgs;
    
            public MethodAndArgsCaller(Method method, String[] args) {
                mMethod = method;
                mArgs = args;
            }
    
            public void run() {
                try {
                    mMethod.invoke(null, new Object[] { mArgs });
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InvocationTargetException ex) {
                    Throwable cause = ex.getCause();
                    if (cause instanceof RuntimeException) {
                        throw (RuntimeException) cause;
                    } else if (cause instanceof Error) {
                        throw (Error) cause;
                    }
                    throw new RuntimeException(ex);
                }
            }
        }
    

    可以看到这里根据传递过来的参数,可知此处通过反射机制调用的是SystemServer.main()方法;到此,总算是进入到了SystemServer类的main()方法

    SystemServer.main

    public static void main(String[] args) {
            new SystemServer().run();
    }
    
    private void run() {
            try {
               	//如果系统时间比1970年早,那就设置为1970
                if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
                    SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
                }
    
                //变更虚拟机的库文件,对于Android 6.0默认采用的是libart.so
                SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
    
                //清除vm内存增长上限,由于启动过程需要较多的虚拟机内存空间
                VMRuntime.getRuntime().clearGrowthLimit();
    
                //设置内存的可能有效使用率为0.8
                VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
    
                // 针对部分设备依赖于运行时就产生指纹信息,因此需要在开机完成前已经定义
                Build.ensureFingerprintProperty();
    
                //访问环境变量前,需要明确地指定用户
                Environment.setUserRequired(true);
    
                // Within the system server, any incoming Bundles should be defused
                // to avoid throwing BadParcelableException.
                BaseBundle.setShouldDefuse(true);
    
                //确保当前系统进程的binder调用,总是运行在前台优先级(foreground priority)
                BinderInternal.disableBackgroundScheduling(true);
    
                // 增加system_server中的binder线程数
                BinderInternal.setMaxThreads(sMaxBinderThreads);
    
                // 创建主线程looper 在当前线程运行
                android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_FOREGROUND);
                android.os.Process.setCanSelfBackground(false);
                Looper.prepareMainLooper();
    
                //初始化android_servers库
                System.loadLibrary("android_servers");
    
                //检测上次关机过程是否失败
                performPendingShutdown();
    
                //初始化系统上下文
                //初始化系统上下文对象mSystemContext,并设置默认的主题,mSystemContext实际上是一个ContextImpl对象。
                //调用ActivityThread.systemMain()的时候,会调用ActivityThread.attach(true),而在attach()里面,则创建了Application对象,并调用了Application.onCreate()。
                createSystemContext();
    
                //创建系统服务管理
                mSystemServiceManager = new SystemServiceManager(mSystemContext);
                LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
            }
    
            //启动各种系统服务
            try {
    	        //引导服务
                startBootstrapServices();
                //核心服务
                startCoreServices();
                //其它服务
                startOtherServices();
            } catch (Throwable ex) {
                throw ex;
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
            }
    
            //开启消息循环
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    system server进程总结:

    • zygote进程通过Zygote.forkSystemServer —> fork.fork()创建system server进程
    • 调用ZygoteInit.handleSystemServerProcess方法设置当前进程名为"system_server",执行dex优化操作,一些属性的初始化,设置binder线程
    • 通过抛出MethodAndArgsCaller异常,回到ZygoteInit.main(),在try catch中执行MethodAndArgsCaller.run;在这里通过反射执行SystemServer.main()方法
    • 在SystemServer.run方法中做一些设置,比如初始化系统上下文 ,创建SystemServiceManager,启动引导服务,启动核心服务,启动其他服务
    • 最后调用Looper.loop(),不断的从消息队列取出消息处理
    展开全文
    qq_30993595 2018-09-16 22:44:25
  • 首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。Android是基于Linux的操作系统,所以init也是Android系统中用户空间的第一个...

    欢迎转载,转载请注明:http://blog.csdn.net/zhgxhuaa


    init启动过程

    众所周知,Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。Android是基于Linux的操作系统,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。下面先简单的看一下init进程的启动过程。


    @/kernel/goodfish/init/main.c

    static int __init kernel_init(void * unused)
    {
    	/*
    	 * Wait until kthreadd is all set-up.
    	 */
    	wait_for_completion(&kthreadd_done);
    	/*
    	 * init can allocate pages on any node
    	 */
    	set_mems_allowed(node_states[N_HIGH_MEMORY]);
    	/*
    	 * init can run on any cpu.
    	 */
    	set_cpus_allowed_ptr(current, cpu_all_mask);
    
    	cad_pid = task_pid(current);
    
    	smp_prepare_cpus(setup_max_cpus);
    
    	do_pre_smp_initcalls();
    	lockup_detector_init();
    
    	smp_init();
    	sched_init_smp();
    
    	do_basic_setup();
    
    	/* Open the /dev/console on the rootfs, this should never fail */
    	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
    		printk(KERN_WARNING "Warning: unable to open an initial console.\n");
    
    	(void) sys_dup(0);
    	(void) sys_dup(0);
    	/*
    	 * check if there is an early userspace init.  If yes, let it do all
    	 * the work
    	 */
    
    	if (!ramdisk_execute_command)
    		ramdisk_execute_command = "/init";
    
    	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
    		ramdisk_execute_command = NULL;
    		prepare_namespace();
    	}
    
    	/*
    	 * Ok, we have completed the initial bootup, and
    	 * we're essentially up and running. Get rid of the
    	 * initmem segments and start the user-mode stuff..
    	 */
    
    	init_post();
    	return 0;
    }
    /* This is a non __init function. Force it to be noinline otherwise gcc
     * makes it inline to init() and it becomes part of init.text section
     */
    static noinline int init_post(void)
    {
    	/* need to finish all async __init code before freeing the memory */
    	async_synchronize_full();
    	free_initmem();
    	mark_rodata_ro();
    	system_state = SYSTEM_RUNNING;
    	numa_default_policy();
    
    
    	current->signal->flags |= SIGNAL_UNKILLABLE;
    
    	if (ramdisk_execute_command) {
    		run_init_process(ramdisk_execute_command);
    		printk(KERN_WARNING "Failed to execute %s\n",
    				ramdisk_execute_command);
    	}
    
    	/*
    	 * We try each of these until one succeeds.
    	 *
    	 * The Bourne shell can be used instead of init if we are
    	 * trying to recover a really broken machine.
    	 */
    	if (execute_command) {
    		run_init_process(execute_command);
    		printk(KERN_WARNING "Failed to execute %s.  Attempting "
    					"defaults...\n", execute_command);
    	}
    	run_init_process("/sbin/init");
    	run_init_process("/etc/init");
    	run_init_process("/bin/init");
    	run_init_process("/bin/sh");
    
    	panic("No init found.  Try passing init= option to kernel. "
    	      "See Linux Documentation/init.txt for guidance.");
    }
    static void run_init_process(const char *init_filename)
    {
    	argv_init[0] = init_filename;
    	kernel_execve(init_filename, argv_init, envp_init);
    }

    在init_post()中会判断execute_command是否为空,如果不为空则执行run_init_process调用。execute_command的赋值在init_setup()中,所以这里应该注意在设置内核启动选项时,应设置为“ init=/init”,以便正常启动init进程,因为编译完Android后生成的文件系统中,init位于最顶层目录。

    <span style="font-size:14px;">static const char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };</span>

    static int __init init_setup(char *str)
    {
    	unsigned int i;
    
    	execute_command = str;
    	/*
    	 * In case LILO is going to boot us with default command line,
    	 * it prepends "auto" before the whole cmdline which makes
    	 * the shell think it should execute a script with such name.
    	 * So we ignore all arguments entered _before_ init=... [MJ]
    	 */
    	for (i = 1; i < MAX_INIT_ARGS; i++)
    		argv_init[i] = NULL;
    	return 1;
    }
    __setup("init=", init_setup);
    当根目录中不存在init时,或者未指定启动项“init=”时,内核会到/sbin、/etc、/bin目录下查找init。

    了解了init进程的启动过程后,接下来看一下init进程都干了些什么?Android中的init进程与Linux不同,其职责可以归结如下:

    • 作为守护进程
    • 解析和执行init.rc文件
    • 生成设备驱动节点
    • 属性服务

    init源码分析

    init进程的入口函数是main,它的代码如下:

    @/system/core/init/init.c

    int main(int argc, char **argv)
    {
        int fd_count = 0;
        struct pollfd ufds[4];
        char *tmpdev;
        char* debuggable;
        char tmp[32];
        int property_set_fd_init = 0;
        int signal_fd_init = 0;
        int keychord_fd_init = 0;
        bool is_charger = false;
    
    
        //启动ueventd
        if (!strcmp(basename(argv[0]), "ueventd"))
            return ueventd_main(argc, argv);
    
    
        //启动watchdogd
        if (!strcmp(basename(argv[0]), "watchdogd"))
            return watchdogd_main(argc, argv);
    
    
        /* clear the umask */
        umask(0);
    
    
            /* 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.
             */
        //创建并挂在启动所需的文件目录
        mkdir("/dev", 0755);
        mkdir("/proc", 0755);
        mkdir("/sys", 0755);
    
    
        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, NULL);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    
    
            /* indicate that booting is in progress to background fw loaders, etc */
        close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));//检测/dev/.booting文件是否可读写和创建
    
    
            /* We must have some place other than / to create the
             * device nodes for kmsg and null, otherwise we won't
             * be able to remount / read-only later on.
             * Now that tmpfs is mounted on /dev, we can actually
             * talk to the outside world.
             */
        open_devnull_stdio();//重定向标准输入/输出/错误输出到/dev/_null_
        klog_init();//log初始化
        property_init();//属性服务初始化
    
    
        //从/proc/cpuinfo中读取Hardware名,在后面的mix_hwrng_into_linux_rng_action函数中会将hardware的值设置给属性ro.hardware
        get_hardware_name(hardware, &revision);
    
    
        //导入并设置内核变量
        process_kernel_cmdline();
    
    
        //selinux相关,暂不分析
        union selinux_callback cb;
        cb.func_log = klog_write;
        selinux_set_callback(SELINUX_CB_LOG, cb);
    
    
        cb.func_audit = audit_callback;
        selinux_set_callback(SELINUX_CB_AUDIT, cb);
    
    
        selinux_initialize();
        /* These directories were necessarily created before initial policy load
         * and therefore need their security context restored to the proper value.
         * This must happen before /dev is populated by ueventd.
         */
        restorecon("/dev");
        restorecon("/dev/socket");
        restorecon("/dev/__properties__");
        restorecon_recursive("/sys");
    
    
        is_charger = !strcmp(bootmode, "charger");//关机充电相关,暂不做分析
    
    
        INFO("property init\n");
        if (!is_charger)
            property_load_boot_defaults();
    
    
        INFO("reading config file\n");
        init_parse_config_file("/init.rc");//解析init.rc配置文件
    
    
        /*
         * 解析完init.rc后会得到一系列的action等,下面的代码将执行处于early-init阶段的action。
         * init将action按照执行时间段的不同分为early-init、init、early-boot、boot。
         * 进行这样的划分是由于有些动作之间具有依赖关系,某些动作只有在其他动作完成后才能执行,所以就有了先后的区别。
         * 具体哪些动作属于哪个阶段是在init.rc中的配置决定的
         */
        action_for_each_trigger("early-init", action_add_queue_tail);
    
    
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        queue_builtin_action(keychord_init_action, "keychord_init");
        queue_builtin_action(console_init_action, "console_init");
    
    
        /* execute all the boot actions to get us started */
        action_for_each_trigger("init", action_add_queue_tail);
    
    
        /* skip mounting filesystems in charger mode */
        if (!is_charger) {
            action_for_each_trigger("early-fs", action_add_queue_tail);
            action_for_each_trigger("fs", action_add_queue_tail);
            action_for_each_trigger("post-fs", action_add_queue_tail);
            action_for_each_trigger("post-fs-data", action_add_queue_tail);
        }
    
    
        /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
         * wasn't ready immediately after wait_for_coldboot_done
         */
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    
    
        queue_builtin_action(property_service_init_action, "property_service_init");
        queue_builtin_action(signal_init_action, "signal_init");
        queue_builtin_action(check_startup_action, "check_startup");
    
    
        if (is_charger) {
            action_for_each_trigger("charger", action_add_queue_tail);
        } else {
            action_for_each_trigger("early-boot", action_add_queue_tail);
            action_for_each_trigger("boot", action_add_queue_tail);
        }
    
    
            /* run all property triggers based on current state of the properties */
        queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    
    
    
    
    #if BOOTCHART
        queue_builtin_action(bootchart_init_action, "bootchart_init");
    #endif
    
    
        for(;;) {//init进入无限循环
            int nr, i, timeout = -1;
            //检查action_queue列表是否为空。如果不为空则移除并执行列表头中的action
            execute_one_command();
            restart_processes();//重启已经死去的进程
    
    
            if (!property_set_fd_init && get_property_set_fd() > 0) {
                ufds[fd_count].fd = get_property_set_fd();
                ufds[fd_count].events = POLLIN;
                ufds[fd_count].revents = 0;
                fd_count++;
                property_set_fd_init = 1;
            }
            if (!signal_fd_init && get_signal_fd() > 0) {
                ufds[fd_count].fd = get_signal_fd();
                ufds[fd_count].events = POLLIN;
                ufds[fd_count].revents = 0;
                fd_count++;
                signal_fd_init = 1;
            }
            if (!keychord_fd_init && get_keychord_fd() > 0) {
                ufds[fd_count].fd = get_keychord_fd();
                ufds[fd_count].events = POLLIN;
                ufds[fd_count].revents = 0;
                fd_count++;
                keychord_fd_init = 1;
            }
    
    
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
    
    
            if (!action_queue_empty() || cur_action)
                timeout = 0;
    
    
    #if BOOTCHART
            if (bootchart_count > 0) {
                if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                    timeout = BOOTCHART_POLLING_MS;
                if (bootchart_step() < 0 || --bootchart_count == 0) {
                    bootchart_finish();
                    bootchart_count = 0;
                }
            }
    #endif
            //等待事件发生
            nr = poll(ufds, fd_count, timeout);
            if (nr <= 0)
                continue;
    
    
            for (i = 0; i < fd_count; i++) {
                if (ufds[i].revents == POLLIN) {
                    if (ufds[i].fd == get_property_set_fd())//处理属性服务事件
                        handle_property_set_fd();
                    else if (ufds[i].fd == get_keychord_fd())//处理keychord事件
                        handle_keychord();
                    else if (ufds[i].fd == get_signal_fd())//处理
                        handle_signal();//处理SIGCHLD信号
                }
            }
        }
    
    
        return 0;
    }

    main函数分析:

      if (!strcmp(basename(argv[0]), "ueventd"))
            return ueventd_main(argc, argv);
    

    main函数一开始就会判断参数argv[0]的值是否等于“ueventd”,如果是就调用ueventd进程的入口函数ueventd_main()启动ueventd进程。这是怎么回事呢?当前正在启动的进程不是init吗?它的名称怎么可能会等于“ueventd”?所以这里有必要看一下ueventd的启动过程,ueventd是在init.rc中被启动的。

    on boot
    service ueventd /sbin/ueventd
        class core
        critical
        seclabel u:r:ueventd:s0
    可以看出ueventd可执行文件位于/sbin/ueventd,在观察了/sbin/ueventd后我们发现,它只不过是 是可执行文件/init的一个符号链接文件,即应用程序ueventd和init运行的是同一个可执行文件。

    所以,整个过程是这样的:内核启动完成之后,可执行文件/init首先会被执行,即init进程会首先被启动。init进程在启动的过程中,会对启动脚本/init.rc进行解析。在启动脚本/init.rc中,配置了一个ueventd进程,它对应的可执行文件为/sbin/ueventd,即ueventd进程加载的可执行文件也为/init(此时init中main函数的参数argv[0] = “/sbin/ueventd”)。因此,通过判断参数argv[0]的值,就可以知道当前正在启动的是init进程还是ueventd进程。      

    PS:ueventd是一个守护进程,主要作用是接收uevent来创建或删除/dev/xxx(设备节点),其实现位于@system/core/init/ueventd.c中。ueventd进程会通过一个socket接口来和内核通信,以便可以监控系统设备事件。


    在开始所有的工作之前,main进程首先做的是创建并挂载启动所需的(其他的会在解析init.rc时创建)文件目录,如下所示:

            /* 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.
             */
        //创建并挂在启动所需的文件目录
        mkdir("/dev", 0755);
        mkdir("/proc", 0755);
        mkdir("/sys", 0755);
    
        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, NULL);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    说明:

    tmpfs是一种虚拟内存的文件系统,典型的tmpfs文件系统完全驻留在RAM中,读写速度远快于内存或硬盘文件系统。

    /dev目录保存着硬件设备访问所需要的设备驱动程序。在Android中,将相关目录作用于tmpfs,可以大幅度提高设备访问的速度。

    devpts是一种虚拟终端文件系统。

    proc是一种虚拟文件系统,只存在于内存中,不占用外存空间。借助此文件系统,应用程序可以与内核内部数据结构进行交互。

    sysfs是一种特殊的文件系统,在Linux 2.6中引入,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息,将proc、devpts、devfs三种文件系统统一起来。
    编译Android系统源码时,在生成的根文件系统中,并不存在/dev、/proc、/sys这类目录,它们是系统运行时的目录,有init进程在运行中生成,当系统终止时,它们就会消失。上面的代码所形成的的文件层次结构为:



    <span style="font-size:14px;">        /* We must have some place other than / to create the
             * device nodes for kmsg and null, otherwise we won't be able to remount / read-only later on.
             * Now that tmpfs is mounted on /dev, we can actually talk to the outside world.
             */
        open_devnull_stdio();//重定向标准输入/输出/错误输出到/dev/_null_</span>
    open_devnull_stdio()函数的作用是重定向标准输入/输出/错误输出到/dev/_null_,至于为什么要重定向的原因在注释中已经写明。open_devnull_stdio()的实现如下:

    @system/core/init/util.c

    void open_devnull_stdio(void)
    {
        int fd;
        static const char *name = "/dev/__null__";
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
            fd = open(name, O_RDWR);
            unlink(name);
            if (fd >= 0) {
                dup2(fd, 0);
                dup2(fd, 1);
                dup2(fd, 2);
                if (fd > 2) {
                    close(fd);
                }
                return;
            }
        }
    
        exit(1);
    }


    <span style="font-size:14px;">klog_init();//log初始化</span>
    klog_init()用于初始化log,通过其实现可以看出log被打印到/dev/__kmsg__文件中。主要在代码中最后通过fcntl和unlink使得/dev/__kmsg__不可被访问,这就保证了只有log程序才可以访问。
    void klog_init(void)
    {
        static const char *name = "/dev/__kmsg__";
    
        if (klog_fd >= 0) return; /* Already initialized */
    
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
            klog_fd = open(name, O_WRONLY);
            if (klog_fd < 0)
                    return;
            fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
            unlink(name);
        }
    }


    property_init
    属性服务初始化,这里先不深究,接下来会单独分析。

    <span style="font-size:14px;">    </span>//从/proc/cpuinfo中读取Hardware名,在后面的mix_hwrng_into_linux_rng_action函数中会将hardware的值设置给属性ro.hardware
        get_hardware_name(hardware, &revision);
    get_hardware_name()函数的作用是从/proc/cpuinfo中获取Hardware和Revision的值,并保持到全局变量hardware和revision中。

    下面的截图是在我的手机上的CPU info信息:


    这里获取hardware信息有什么用呢?在main()函数后面的代码中,我们可以看见这样一句:

        //导入并设置内核变量
        process_kernel_cmdline();
    下面看一下process_kernel_cmdline的实现:

    @system/core/init/init.c

    static void process_kernel_cmdline(void)
    {
        /* don't expose the raw commandline to nonpriv processes */
        chmod("/proc/cmdline", 0440);
    
        /* first pass does the common stuff, and finds if we are in qemu.
         * second pass is only necessary for qemu to export all kernel params
         * as props.
         */
        import_kernel_cmdline(0, import_kernel_nv);
        if (qemu[0])
            import_kernel_cmdline(1, import_kernel_nv);
    
        /* now propogate the info given on command line to internal variables
         * used by init as well as the current required properties
         */
        export_kernel_boot_props();
    }
    static void export_kernel_boot_props(void)
    {
        char tmp[PROP_VALUE_MAX];
        
        ......
    
        /* if this was given on kernel command line, override what we read
         * before (e.g. from /proc/cpuinfo), if anything */
        ret = property_get("ro.boot.hardware", tmp);
        if (ret)
            strlcpy(hardware, tmp, sizeof(hardware));
        property_set("ro.hardware", hardware);
    
        snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
        property_set("ro.revision", tmp);
    
        ......
    }
    process_kernel_cmdline()函数用于导入和设置一些内核变量,在export_kernel_boot_props()中我们看见将hardware的值赋值给了属性"ro.hardware"。那这个赋值又是干什么的呢?我们再看一下main()函数,在解析init.rc配置文件的时候,有没有发现少了点什么?

        INFO("reading config file\n");
        init_parse_config_file("/init.rc");//解析init.rc配置文件
    是的,在以前比较老的代码中(例如2.3和4.0)这里除了init.rc以外还会有一个与硬件相关的rc脚本,如下:

    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); 
    init_parse_config_file(tmp); 
    那现在这段代码跑去哪里了呢?我们在init.rc中找到了它:

    所以,之前设置的ro.hardware的值是在这里用的,在init.rc中用来导入init.${ro.hardware}.rc脚本,然后一起进行解析。与之前相比,这里只是方式变了,本质上还是一样的。


        INFO("reading config file\n");
        init_parse_config_file("/init.rc");//解析init.rc配置文件
    
        action_for_each_trigger("early-init", action_add_queue_tail);
    
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
        queue_builtin_action(keychord_init_action, "keychord_init");
        queue_builtin_action(console_init_action, "console_init");
    
        /* execute all the boot actions to get us started */
        action_for_each_trigger("init", action_add_queue_tail);
    
        /* skip mounting filesystems in charger mode */
        if (!is_charger) {
            action_for_each_trigger("early-fs", action_add_queue_tail);
            action_for_each_trigger("fs", action_add_queue_tail);
            action_for_each_trigger("post-fs", action_add_queue_tail);
            action_for_each_trigger("post-fs-data", action_add_queue_tail);
        }
    
        /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
         * wasn't ready immediately after wait_for_coldboot_done
         */
        queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    
        queue_builtin_action(property_service_init_action, "property_service_init");
        queue_builtin_action(signal_init_action, "signal_init");
        queue_builtin_action(check_startup_action, "check_startup");
    
        if (is_charger) {
            action_for_each_trigger("charger", action_add_queue_tail);
        } else {
            action_for_each_trigger("early-boot", action_add_queue_tail);
            action_for_each_trigger("boot", action_add_queue_tail);
        }
    
            /* run all property triggers based on current state of the properties */
        queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    
    
    #if BOOTCHART
        queue_builtin_action(bootchart_init_action, "bootchart_init");
    #endif
    这部分代码用于解析init.rc脚本,并触发执行解析生成的action。这部分后面单独进行分析。

    在main()函数的最后,init进入了一个无限循环,并等待一些事情的发生。即:在执行完前面的初始化工作以后,init变为一个守护进程。init所关心的事件有三类:属性服务事件、keychord事件和SIGNAL,当有这三类事件发生时,init进程会调用相应的handle函数进行处理。
    展开全文
    zhgxhuaa 2014-04-05 20:41:11
  • weixin_40535588 2021-11-16 11:42:57
  • 1.61MB flykate1982 2016-07-28 18:05:55
  • marshal_zsx 2018-05-27 13:45:03
  • 96KB snail201211 2018-01-22 10:19:22
  • tankai19880619 2013-04-28 03:24:50
  • zhonglunshun 2017-11-23 16:14:40
  • u013275111 2017-02-23 10:51:49
  • hxpjava1 2018-04-05 13:14:32
  • z240336124 2019-08-04 18:30:30
  • Gaugamela 2018-02-08 11:25:04
  • 3星
    515KB ans4 2011-07-31 07:30:12
  • qq_37186884 2021-03-11 20:14:21
  • bailyzheng 2015-03-29 17:59:20
  • qq_21586317 2019-03-11 23:59:59
  • Eric_Hxy 2012-11-12 17:59:13
  • qq_41453285 2019-10-14 12:45:16
  • Guet_Kite 2019-02-23 21:12:23
  • lijie2664989 2020-10-25 15:28:47
  • zhonglunshun 2017-11-23 15:10:42
  • cagent_z 2017-03-11 23:15:51
  • tkwxty 2020-05-13 14:33:28
  • gatieme 2016-05-29 17:01:19
  • u012417380 2017-11-13 11:16:13
  • agave7 2019-02-11 11:54:17

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 278,979
精华内容 111,591
关键字:

启动init进程前