【Android底层学习总结】5. init进程的启动

1. 前言

前面我们已经了解了内核的初始化,还是比较复杂的,具体的三言两语也解释不清,只能好好分析源码;这节我们该往上层走了,这一节我们来研究init进程的初始化和启动,其实事实上前面的内核初始化已经进入init初始化了。

2. 源码解析

注:本系列源码都是基于kernel4.9的
前面我们已经讲到rest_init函数很重要,我们init进程的初始化启动也在这里面。
下面我们依次进入以下文件夹或函数:

kernel4.9->init->main.c->start_kernel->rest_init:

在rest_init函数里面有这样一行代码:

kernel_thread(kernel_init, NULL, CLONE_FS);

这行代码就是创建init进程的关键,kernel_init就是我们的init进程,kernel_thread就是用来创建进程的,这里返回值应该是1,代表了进程号。在这行代码以下有这样一行代码:

pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

这行代码就是用来创建pid为2的kthreadd进程,用来做内核进程的处理。
而我们的init进程主要用来做用户态进程的处理的。
现在我们进入kernel_init函数
它位于kernel4.9/init/目录下,源码如下:

static int __ref kernel_init(void *unused)
{
	int ret;
	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_readonly();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	rcu_end_inkernel_boot();
#ifdef CONFIG_MTPROF
	// 到这里代表内核初始化完成,同时init也初始化完毕,下面就会进行运行
	log_boot("Kernel_init_done");
#endif
	if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * 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) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

先不管下面的代码,我们先进入kernel_init_freeable();函数看看,我们看到在前两节分析过的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)
		pr_err("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;
		// 准备命名空间,其实就是挂载初始化root文件系统,加载运存等等
		prepare_namespace();
	}

下面是prepare_namespace();的源码,有兴趣可以看看根文件系统挂载流程:

/*
 * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
 */
void __init prepare_namespace(void)
{
	int is_floppy;

	if (root_delay) {
		printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
		       root_delay);
		ssleep(root_delay);
	}

	/*
	 * wait for the known devices to complete their probing
	 *
	 * Note: this is a potential source of long boot delays.
	 * For example, it is not atypical to wait 5 seconds here
	 * for the touchpad of a laptop to initialize.
	 */
	wait_for_device_probe();

	md_run_setup();
	dm_run_setup();

	if (saved_root_name[0]) {
		root_device_name = saved_root_name;
		if (!strncmp(root_device_name, "mtd", 3) ||
		    !strncmp(root_device_name, "ubi", 3)) {
			mount_block_root(root_device_name, root_mountflags);
			goto out;
		}
		ROOT_DEV = name_to_dev_t(root_device_name);
		if (strncmp(root_device_name, "/dev/", 5) == 0)
			root_device_name += 5;
	}

	if (initrd_load())
		goto out;

	/* wait for any asynchronous scanning to complete */
	if ((ROOT_DEV == 0) && root_wait) {
		printk(KERN_INFO "Waiting for root device %s...\n",
			saved_root_name);
		while (driver_probe_done() != 0 ||
			(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
			msleep(100);
		async_synchronize_full();
	}

	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

	if (is_floppy && rd_doload && rd_load_disk(0))
		ROOT_DEV = Root_RAM0;

	mount_root();
out:
	devtmpfs_mount("dev");
	sys_mount(".", "/", NULL, MS_MOVE, NULL);
	sys_chroot(".");
}

当以上工作做完,init初始化就已经差不多了,下面就是运行init进程:
kernel_init函数里面,有以下部分代码:

if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * 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) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	// 如果在前面的启动init进程程序中未能成功启动init,
	//就依次在以下路径查找并运行init
	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    
	    !try_to_run_init_process("/bin/sh")) // 试图建立/bin/sh 来代替init程序
		return 0;
	
	// 如果没有找到任何一个init程序就执行panic,最后可能会重启计算机
	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

这些代码就是用来启动init进程的,至此init进程已经成功启动,init启动后就可以使用用户态的命令程序了。

3. 总结

以上我们描述了ini进程的启动,init的源码我们在以后的总结中会继续研究,其实吧,上面的叙述不够详细;应当在源码了解更多的细节,本系列主要用途是带领大家研究源码,致力于大概了解安卓系统启动过程中要做哪些事情。

本系列代码栏目链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值