正点原子linux内核启动流程学习之 init进程、kthreadd进程、和idle进程

1.* idle进程 PID = 0

idle进程 由系统自动创建, 运行在内核态
idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。完成加载系统后,演变为进程调度、交换

2. init进程

init进程由idle通过kernel_thread创建,在内核空间完成初始化后, 加载init程序, 并最终用户空间

由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程
Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。

2.1 (kernel_init 函数)

static int __ref kernel_init(void *unused)
{
	int ret;

	kernel_init_freeable();  	/* init 进程的一些其他初始化工作 */
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();	/* 等待所有的异步调用执行完成 */
	free_initmem();				/* 释放 init 段内存 */
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;		/* 标记系统正在运行 */
	numa_default_policy();

	flush_delayed_fput();
	/* ramdisk_execute_command 是一个全局的 char 指针变量,此变量值为“/init”,
	* 也就是根目录下的 init 程序。 ramdisk_execute_command 也可以通过 uboot 传递,
	* 在 bootargs 中 使用“rdinit=xxx”即可, xxx 为具体的 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.
	 */
	 
	 /* 如果 ramdisk_execute_command 为空的话就看 execute_command 是否为空,反
	  * 正不管如何一定要在根文件系统中找到一个可运行的 init 程序。 execute_command 的值是通过
	  * uboot 传递,在 bootargs 中使用“init=xxxx”就可以了,比如“init=/linuxrc”表示根文件系统中
	  * 的 linuxrc 就是要执行的用户空间 init 程序
	 */
	 
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}
	/* ramdisk_execute_command 和 execute_command 都为空 */
	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;
	/* 如果以上步骤都没有找到用户空间的 init 程序,那么就提示错误发生! */
	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

2.2 kernel_init_freeable 函数

static noinline void __init kernel_init_freeable(void)
{
	/*
	 * Wait until kthreadd is all set-up.
	 */
	wait_for_completion(&kthreadd_done); /* 等待 kthreadd 进程准备就绪 */

	/* Now the scheduler is fully set up and can do blocking allocations */
	gfp_allowed_mask = __GFP_BITS_MASK;

	/*
	 * init can allocate pages on any node
	 */
	set_mems_allowed(node_states[N_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();					/* SMP 初始化 */
	sched_init_smp();			/* 多核(SMP)调度初始化 */

	do_basic_setup();			/* 设备初始化都在此函数中完成 */
	/*
	 打开设备“/dev/console”,在 Linux 中一切皆为文件!因此“/dev/console”也
	 是一个文件,此文件为控制台设备。每个文件都有一个文件描述符,此处打开的“/dev/console”
	 文件描述符为 0,作为标准输入(0)。
   */
	/* 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");

	/*
		sys_dup 函数将标准输入(0)的文件描述符复制了 2 次,一个作为标准
		输出(1),一个作为标准错误(2)。
	*/
	(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 来挂载根文件系统。跟文件系统也是由命令行参
数指定的,也就是 uboot 的 bootargs 环境变量*/
		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..
	 *
	 * rootfs is available now, try loading the public keys
	 * and default modules
	 */

	integrity_load_keys();
	load_default_modules();
}

do_basic_setup 函数用于完成 Linux 下设备驱动初始化工作!非常重要。
do_basic_setup 会调用 driver_init 函数完成 Linux 下驱动模型子系统的初始化。

3. kthreadd进程

由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理
它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值