栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux内核启动过程分析(三)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux内核启动过程分析(三)

接上篇:linux内核启动过程分析(二)

2.linux启动过程C语言阶段
initmain.c -> void start_kernel(void)
本文目的在于理清内核的启动流程,再者好多函数本人也未做了解,所以下面只对部分函数做简要说明。注:部分解释来源于网络。

asmlinkage __visible void __init start_kernel(void)
{
	char *command_line;
	char *after_dashes;

	
	lockdep_init();
	
	set_task_stack_end_magic(&init_task);
	
	smp_setup_processor_id();
	debug_objects_early_init();

	
	boot_init_stack_canary();

	cgroup_init_early();

	
	local_irq_disable();
	early_boot_irqs_disabled = true;


	boot_cpu_init();
	page_address_init();
	pr_notice("%s", linux_banner);  
	
	setup_arch(&command_line);
	......
	
	
	rest_init();
}

Linux中有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2)。其中之前的分析的所有启动阶段的代码以及start_kernel代码都是在0号进程下运行的。0号进程最终在《kernelschedidle.c》的cpu_idle_loop中调用一个while(1)死循环用作空闲进程。当无其它进程运行时,系统将调用空闲进程运行。反之空闲进程将被挂起,切换至其它进程运行。

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

	rcu_scheduler_starting();
	smpboot_thread_init();
	
	
	kernel_thread(kernel_init, NULL, CLONE_FS);
	numa_default_policy();
	
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	complete(&kthreadd_done);

	
	init_idle_bootup_task(current);
	schedule_preempt_disabled();
	
	
	cpu_startup_entry(CPUHP_ONLINE);
}

上面rest_init函数中系统通过调用kernel_thread来创建kernel_init(pid=1)、kthreadd(pid=2)。所以可以看出1号、2号进程的父进程是0号进程。1号进程最终会调用文件系统中的init进程,init进程不会返回,并且管理着用户空间的所有进程,是所有用户进程都直接或间接的来自init进程的创建。
kernel_init进程《initmain.c》

static int __ref kernel_init(void *unused)
{
	int ret;
	
	kernel_init_freeable();
	
	async_synchronize_full();  
	free_initmem();			 
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

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

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

static noinline void __init kernel_init_freeable(void)
{
	
	 
	wait_for_completion(&kthreadd_done);

	
	gfp_allowed_mask = __GFP_BITS_MASK;

	
	set_mems_allowed(node_states[N_MEMORY]);
	
	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();

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

	

	integrity_load_keys(); 
	load_default_modules();  
}

kthreadd进程《kernelkthread.c》是所有内核线程的父线程,用来管理、创建和调度其它内核线程kernel_thread,会循环执行一个kthreadd的函数,该函数的作用就是查询kthread_create_list全局链表中维护的kthread, 当kthread_create_list非空时调用kernel_thread创建相应的内核线程,为空时则进行一次系统调度,因此所有的内核线程都是直接或者间接的以kthreadd为父进程

int kthreadd(void *unused)
{
	struct task_struct *tsk = current;

	
	set_task_comm(tsk, "kthreadd");
	ignore_signals(tsk);
	set_cpus_allowed_ptr(tsk, cpu_all_mask);
	set_mems_allowed(node_states[N_MEMORY]);

	current->flags |= PF_NOFREEZE;

	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);  
		if (list_empty(&kthread_create_list))
			
			schedule();
		__set_current_state(TASK_RUNNING);

		spin_lock(&kthread_create_lock);
		while (!list_empty(&kthread_create_list)) {
			struct kthread_create_info *create;
			
			create = list_entry(kthread_create_list.next,
					    struct kthread_create_info, list);
			list_del_init(&create->list);
			spin_unlock(&kthread_create_lock);

			create_kthread(create);  

			spin_lock(&kthread_create_lock);
		}
		spin_unlock(&kthread_create_lock);
	}

	return 0;
}

上面可以看出这个内核进程的创建过程主要集中在kthread_create_list这个全局的链表中,那么这个kthread_create_list链表是在什么地方添加kthread_create_info 信息的呢?在《kernelkthread.c》搜索kthread_create_list可以在文件的开头看见其定义,并且用static进行了修饰,说明kthread_create_list只是当前的文件中被使用,当前文件在继续搜索可以找到在kthread_create_on_node函数中利用list_add_tail将一个kthread_create_info添加到了kthread_create_list的尾部。所有要想创建一个内核进程可以通过调用kthread_create_on_node函数来实现。
kthread_create_list定义的展开

static LIST_HEAD(kthread_create_list);
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) 
	struct list_head name = LIST_HEAD_INIT(name)	
struct list_head {
	struct list_head *next, *prev;
};

展开的结果:
第一层
static struct list_head kthread_create_list = LIST_HEAD_INIT(kthread_create_list)
第二层
static struct list_head kthread_create_list = { 
	next = &kthread_create_list,
	prev = &kthread_create_list,
};

至此内核启动的大致流程已完…

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/731173.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号