从源码角度看Android系统init进程启动过程

init进程是Linux系统中用户空间的第一个进程,进程号为1。Kernel启动后,在用户空间启动init进程,并调用/system/core/init.cpp中的main方法执行一些重要的工作。

备注:本文将结合Android8.0的源码看init进程的启动过程以及init进程做了哪些重要工作。

1. init进程启动前系统的启动流程

在引入init进程前,我们需要大致了解系统是如何走到init进程的。大致步骤如下:

  1. 启动电源和系统启动

    按下电源,让设备开机时引导芯片代码会从预定义的地方开始执行。加载引导程序BootLoader到RAM中。

  2. BootLoader

    BootLoader只是Android系统开始前的一个引导程序,主要作用是将OS系统拉起来并运行。

  3. 启动Linux内核

    当内核启动时,会先去设置缓存、加载驱动等,在系统设置完成后,会在系统文件中寻找init.rc文件,并启动init进程。

  4. 启动init进程

    经过前面的步骤,到这一步,init就被正式启动。

init进程启动后,下面就看下它里面的一些重要的职责工作。

2. init进程main函数

main函数是init进程的入口函数,代码位置在:

/system/core/init.cpp

深入到init.cpp的main函数中:

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

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

       // Clear the umask.
       umask(0);

       //创建和挂载启动所需的文件目录
       mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
       mkdir("/dev/pts", 0755);
       mkdir("/dev/socket", 0755);
       mount("devpts", "/dev/pts", "devpts", 0, NULL);
       #define MAKE_STR(x) __STRING(x)
       mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
       // Don't expose the raw commandline to unprivileged processes.
       chmod("/proc/cmdline", 0440);
       gid_t groups[] = { AID_READPROC };
       setgroups(arraysize(groups), groups);
       mount("sysfs", "/sys", "sysfs", 0, NULL);
       mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
       mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
       mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
       mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

       //初始化Kernel的Log
       InitKernelLogging(argv);

       ...省略...
	   
    }

    ...省略...

	//创建一块共享的内存空间,用于对属性服务进行初始化
    property_init();

    ...省略...

	//创建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(ERROR) << "epoll_create1 failed";
        exit(1);
    }

	//初始化子进程退出的信号处理函数
	//如果子进程异常退出,init进程会调用该函数中设定的信号处理函数来进程处理
	//防止子进程成为僵尸进程
    signal_handler_init();

	//加载default.prop文件,导入默认的环境变量
    property_load_boot_defaults();
    export_oem_lock_status();
	
	//启动属性服务器,会调用epoll_ctl设置property fd可读的回调函数
    start_property_service();
    
	...省略...
	
    if (bootscript.empty()) {
	
		//解析init.rc文件
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }

    ...省略...

    //不要在充电器模式下挂载文件系统或启动核心系统服务
    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.
        int epoll_timeout_ms = -1;

        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
		
			//重启需要重启的服务
            restart_processes();

            // If there's a process that needs restarting, wake up in time for that.
            if (process_needs_restart_at != 0) {
                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }

        epoll_event ev;
		//循环等待事件发生
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

init进程执行完成后,就进入循环等待epoll_wait的状态。init的main函数中做了大量初始化工,比较复杂。我主要学习了如下几个关键点:

  1. 创建和挂载启动的文件

  2. 属性服务

  3. 进程信号处理

  4. 解析init.rc文件

  5. init启动Zygote

3. 创建和挂载启动的文件

在最开始的时候就创建和挂载启动所需要的文件目录,其中挂载了:

  • tempfs

  • devpts

  • proc

  • sysfs

  • selinuxfs

共挂载了这五个系统,这些都是系统运行时的目录,只有当系统运行时才会存在,当系统停止时,这些目录就不会存在。

4. 属性服务

在Windows上有一个注册表管理器,主要采用的是键值对的形式来记录用户、软件的一些信息。即使系统或软件重启,还是能根据之前注册表中的记录,进行初始化工作。在Android系统中,也有一个类似的机制,叫作属性服务。

当某个进程通过property_set()方法修改属性后,init进程会先检查权限,当权限验证通过后,就会去修改相应的属性值,而属性值一旦改变,就会触发相应的触发器(即rc文件中的on开头的语句),在Android Shared Memmo

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值