linux kernel_init

本文详细解析了Linux内核启动过程中关键线程的创建及作用,重点介绍了kernel_init线程如何创建1号进程并完成内核驱动模块的注册,以及内核如何进行基本设置和启动默认控制台。

前言:

 

内核在启动用户空间程序时会创建两个线程,kthread() 和 kernel_init()线程,在前一篇介绍了kthread()线程 点击打开链接,本文不在赘述,这里主要是对kernel_init()线程创建init=1号进程,并完成驱动模块的注册(这里的驱动模块是编译进内核的模块)。

 

1. kthreadd()

路径:linux-3.10.x\init\main.c

 

static noinline void __init_refok rest_init(void)
{
	int pid;

	rcu_scheduler_starting();
	/*
	 * We need to spawn init first so that it obtains pid 1, however
	 * the init task will end up wanting to create kthreads, which, if
	 * we schedule it before we create kthreadd, will OOPS.
	 */
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //init=1 号进程的创建
	numa_default_policy();
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //内核进程kthread的创建,用来管理内核层线程
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	complete(&kthreadd_done); //唤醒完成量

	/*
	 * The boot idle thread must execute schedule()
	 * at least once to get things moving:
	 */
	init_idle_bootup_task(current);
	schedule_preempt_disabled();
	/* Call into cpu_idle with preempt disabled */
	cpu_startup_entry(CPUHP_ONLINE);
}

 

2. kernel_init()

 路径:linux-3.10.x\init\main.c

功能:init=1号进程创建、内核驱动模块注册、启动默认控制台/dev/console

static int __ref kernel_init(void *unused)
{
	//kernel_init_freeable内部使用了等待完成量“kthreadd_done”,通过内核启动的报文我们得知,该线程虽然先创建
	//但却是在“kthreadd”线程创建完才执行的,原因是kthreadd执行完后才唤醒完成量的,所以这里会等待完成量!!!
	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem(); //释放init初始化数据段,见下面
	mark_rodata_ro(); //标记只读数据段为只读,arm平台不影响
	system_state = SYSTEM_RUNNING;//设置系统为运行状态
	numa_default_policy(); //numa即非一致性内存访问,设置默认策略

	flush_delayed_fput();

	/*
	内核启动信息“Kernel command line: root=/dev/mtdblock4 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0,115200n8 rdinit=/sbin/init ro mem=64M”
	
	如果ramdisk_execute_command变量指定了要运行的程序,启动它。
  	ramdisk_execute_command的取值分为三种情况:
  	a.如果命令行参数中指定了“rdinit=...”,则ramdisk_execute_command等于这个参数指定的程序。
  	b.否则,如果/init程序存在,ramdisk_execute_command就等于/init
  	c.否则,ramdisk_execute_command为空
  	韦老师书中所用的命令没有设定“rdinit=...”,根文件系统中也没有"/init"。所以ramdisk_execute_command为空。if下的那块程序不会执行。
	*/
	if (ramdisk_execute_command) {
		if (!run_init_process(ramdisk_execute_command))
			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) {
		if (!run_init_process(execute_command))
			return 0;
		pr_err("Failed to execute %s.  Attempting defaults...\n",
			execute_command);
	}

	/*
		分析文件系统中init源码,里面主要是1号进程创建,环境设置,还有文件系统的console,
	*/
	
	if (!run_init_process("/sbin/init") ||
	    !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.");
}
即非一致性内存访问,设置默认策略

	flush_delayed_fput();

	/*
	内核启动信息“Kernel command line: root=/dev/mtdblock4 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0,115200n8 rdinit=/sbin/init ro mem=64M”
	
	如果ramdisk_execute_command变量指定了要运行的程序,启动它。
  	ramdisk_execute_command的取值分为三种情况:
  	a.如果命令行参数中指定了“rdinit=...”,则ramdisk_execute_command等于这个参数指定的程序。
  	b.否则,如果/init程序存在,ramdisk_execute_command就等于/init
  	c.否则,ramdisk_execute_command为空
  	韦老师书中所用的命令没有设定“rdinit=...”,根文件系统中也没有"/init"。所以ramdisk_execute_command为空。if下的那块程序不会执行。
	*/
	if (ramdisk_execute_command) {
		if (!run_init_process(ramdisk_execute_command))
			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) {
		if (!run_init_process(execute_command))
			return 0;
		pr_err("Failed to execute %s.  Attempting defaults...\n",
			execute_command);
	}

	/*
		分析文件系统中init源码,里面主要是1号进程创建,环境设置,还有文件系统的console,
	*/
	
	if (!run_init_process("/sbin/init") ||
	    !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.");
}

 

void free_initmem(void)
{
#ifdef CONFIG_HAVE_TCM
	extern char __tcm_start, __tcm_end;

	poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start);
	free_reserved_area(&__tcm_start, &__tcm_end, 0, "TCM link");
#endif

	poison_init_mem(__init_begin, __init_end - __init_begin); //释放init段
	if (!machine_is_integrator() && !machine_is_cintegrator())
		free_initmem_default(0);
}
/*
 * Poison init memory with an undefined instruction (ARM) or a branch to an
 * undefined instruction (Thumb).
 */
static inline void poison_init_mem(void *s, size_t count)
{
	u32 *p = (u32 *)s;
	for (; count != 0; count -= 4)
		*p++ = 0xe7fddef0;
		/*
		内核初始化时把这些内存区域初始化为0xe7fddef0 (an undefined instruction (ARM) or 
		a branch to an undefined instruction (Thumb)),如果运行时函数非法访问到了这些区域,
		会触发一个undef instruction的异常并打印相应的回调,从而辅助开发人员更快地解决相关
		问题。
		*/
}

 

 

3. kernel_init_freeable()

路径:linux-3.10.x\init\main.c

功能:kernel_init_freeable主要功能是,等待内核线程创建完wait_for_completion(&kthreadd_done)、注册内核驱动模块do_basic_setup()、启动默认控制台/dev/console

 

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();
	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)
		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;
		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 default modules */
	load_default_modules();
}

 

 

4. do_basic_setup()

路径:linux-3.10.x\init\main.c

功能:初始化cpuset子系统、创建khelper线程队列、内核模块驱动注册、

 

static void __init do_basic_setup(void)
{
	cpuset_init_smp(); //初始化内核control group的cpuset子系统
	usermodehelper_init();//创建khelper单线程工作队列,用于协助新建和运行用户空间程序
	shmem_init(); //初始化共享内存
	driver_init(); //驱动模块总线、文件系统注册,包括bus、devtmpfs、platform
	init_irq_proc(); //创建/proc/irq目录, 并初始化系统中所有中断对应的子目录
	do_ctors(); //执行内核的构造函数
	usermodehelper_enable(); //使能usermodehelper
	do_initcalls(); //调用level 0到level 7的initcall函数,依次的level名称是"early", "core", "postcore", "arch", "subsys", "fs", "device", “late”,需要注意的kernel在这块的命名有些问题,early_initcall对应的level小于0,pure_initcall对应level才是0
	random_int_secret_init(); //初始化随机数生成池
}

 

 

void __init driver_init(void)
{
	/* These are the core pieces */
	devtmpfs_init();
	devices_init();
	buses_init();
	classes_init();
	firmware_init();
	hypervisor_init();

	/* These are also core pieces, but must come after the
	 * core core pieces.
	 */
	platform_bus_init(); //见此文分析:http://blog.csdn.net/xichangbao/article/details/52938240
	cpu_dev_init();
	memory_dev_init();
}

 

 

 

5. do_initcalls()路径:linux-3.10.x\init\main.c

 

 

功能:初始化initcall_levels,完成编译进内核的驱动模块注册

 

static void __init do_initcalls(void)
{
	int level;

	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
}

由于这里涉及到的东西比较多,可以见之前写的一篇博客点击打开链接,本文不再赘述!

至此,内核的初始化结束,正式进入了用户空间的初始化过程至此,内核的初始化结束,正式进入了用户空间的初始化过程至此,内核的初始化结束,正式进入了用户空间的初始化过程,在kernel_init线程中调用的do_basic_setup()函数会去初始化设备驱动,完成其他驱动程序(直接编译进内核的模块)的初始化。内核中大部分的启动数据输出(都是各设备的驱动模块输出)都是这里产生的。是我们驱动工程师需要重点关注的函数。




 

<think>好的,我需要回答用户关于Linux内核初始化中`kernel_init_done`的含义和作用的问题。首先,我需要回忆一下Linux内核启动过程的相关知识。 用户提供的引用中提到了`kernel_init_freeable`函数,其中调用了`wait_for_completion(&kthreadd_done)`,这说明在内核初始化过程中,完成量(completion)机制被用来同步不同阶段的执行。`kthreadd_done`可能表示kthreadd线程已经完成初始化,之后`kernel_init_freeable`才能继续执行。 现在用户问的是`kernel_init_done`,虽然没有在提供的引用中直接提到,但根据命名惯例,`kernel_init_done`可能是一个完成量,用于表示内核初始化阶段的完成。通常,这种完成量会被其他子系统或模块用来等待内核初始化完成后再执行自己的任务。 接下来,我需要确认`kernel_init_done`的实际存在和用途。根据Linux内核代码的结构,完成量通常使用`struct completion`类型,并通过`init_completion()`初始化,`complete()`触发完成,`wait_for_completion()`等待完成。例如,`kernel_init`函数在完成初始化后可能会调用`complete(&kernel_init_done)`,而需要等待该事件的模块会调用`wait_for_completion(&kernel_init_done)`。 此外,用户提供的引用[2]提到了`initcall_run_list`函数,该函数负责执行初始化函数列表。这可能是在内核初始化的后期阶段,当所有初始化函数执行完毕后,触发`kernel_init_done`完成量,通知其他部分内核初始化已完成。 总结来说,`kernel_init_done`的作用是作为内核初始化完成的信号机制,确保依赖内核初始化的模块或进程在继续执行前等待这一阶段的完成。这有助于同步和避免竞态条件,确保系统正确启动。</think>### Kernel_init_done 的定义及作用 在 Linux 内核初始化过程中,`kernel_init_done` 是一个完成量(Completion)机制,用于**同步内核初始化阶段的完成状态**。它通常在 `init/main.c` 中定义,属于内核启动流程中的关键同步点。 #### 1. 定义与初始化 `kernel_init_done` 的定义通常如下(伪代码): ```c static struct completion kernel_init_done; ``` 在内核初始化函数(如 `kernel_init`)的早期阶段,会通过 `init_completion()` 进行初始化: ```c init_completion(&kernel_init_done); ``` #### 2. 作用与触发时机 - **作用**: 标记内核初始化完成,通知依赖此状态的子系统或模块可以继续执行。例如: - 用户空间初始化(如 `systemd` 或 `init` 进程)可能需要等待内核初始化完成后再启动。 - 某些驱动或内核线程需确保内核核心功能已就绪。 - **触发时机**: 在 `kernel_init` 函数的末尾(所有关键初始化完成后)调用: ```c complete(&kernel_init_done); ``` #### 3. 使用场景示例 其他模块可通过 `wait_for_completion()` 等待该事件: ```c wait_for_completion(&kernel_init_done); ``` 例如,用户空间初始化进程可能通过此机制确保内核已准备好环境[^1][^2]。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值