Linux内核初始化步骤(一)

本文参考了https://blog.csdn.net/qing_ping/article/details/17351017博主的内容。
内核的初始化过程由start_kernel函数(\init\main.c)开始,至第一个用户进程init结束,调用了一系列的初始化函数对所有的内核组件进行初始化。其中,start_kernel、rest_init、kernel_init、init_post4个函数构成了整个初始化过程的主线。下面先来分析start_kernel函数。

asmlinkage void __init start_kernel(void)
{
	char * command_line;
	extern const struct kernel_param __start___param[], __stop___param[];
	/*这两个变量为地址指针,指向内核启动该参数处理相关结构体段在内存中位置(虚拟地址)。
	声明传入参数的外部参数对于arm平台,位于\include\asm-generic\vmlinux.lds.h*/
	/*
	 * Need to run as early as possible, to initialize the
	 * lockdep hash:
	 */
 	 /*函数中初始化了两个哈希链表,分别是classhash_table和chainhash_table,
 	 主要是用来调试内核锁 的,比如检测加锁的顺序看是否有发生死锁的可能,
 	 具体的作用要看后面怎么使用这两个哈希链表。*/
	lockdep_init();
	
	  /*当只有一个CPU的时候,这个函数什么都不做,但是如果有多个CPU的
	  时候,那么它就返回在启动的时候那个CPU的代号*/
	smp_setup_processor_id();
	
	  /*该函数的作用是初始化obj_hash、obj_static_pool这2个全局变量*/
	debug_objects_early_init();
  
	 /* Set up the the initial canary ASAP:
	 */
	 /*该函数主要用于初始化“金丝雀“——canary的值。用于防止栈溢出攻击。*/
	boot_init_stack_canary();
	
	/*1.cgroup:它的全称为control group。即一组进程的行为控制。
	2.该函数主要是做数据结构和其中链表的初始化
	*/
	cgroup_init_early();
	
	/*关闭当前CPU的中断,即系统总中断(底层调用汇编指令)*/
	local_irq_disable();
	early_boot_irqs_disabled = true;

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 	/*注册clockevents框架*/
	tick_init();
	
	/*激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态*/
	boot_cpu_init();
	
	/*初始化页地址,使用链表将其链接起来*/
	page_address_init();
	
	/*显示内核的版本信息*/
	printk(KERN_NOTICE "%s", linux_banner);
	
	/*内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了
	处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、
	内存子系统的早期的初始化(bootmem分配器)。 主要完成了4个方面的工作,
	一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值给kernel相应的全
	局变量,然后呢是对boot_command_line和tags接行解析,再然后就是
	memory、cache的初始化,最后是为kernel的后续运行请求资源*/
	setup_arch(&command_line);
	
	/*1.初始化代表内核本身内存使用的管理结构体init_mm。
	2.每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct
	3.设置成员变量*mmap指向自己,意味着内核只有一个内存管理结构
	4.设置*pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16K,
	所以init_mm定义了整个kernel的内存空间)
	5.这些内容涉及到内存管理子系统
	mm_init_owner(&init_mm, &init_task);
	
	/*初始化CPU屏蔽字*/
	mm_init_cpumask(&init_mm);
	
	/*对cmdline进行备份和保存:保存未改变的command_line到字符数组static_command_line[]中,
	保存boot_command_line到字符数组saved_command_line[]中*/
	setup_command_line(command_line);
	
	/*下面三段式针对SMP处理器的内存初始化函数,对arm来说是空*/
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */

	/*建立系统内存页区(zone)链表*/
	build_all_zonelists(NULL);
	
	/*内存页初始化*/
	page_alloc_init();
	
	/*打印Linux启动命令行参数*/
	printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
	/*Kernel command line: console=ttyS1,115200n8 //调试串口为UART1,波特率为115200n8
	mem=32M@0xc0000000 mem=192M@0xc4000000 //DDR内存分配,为arm分配了224M
	eth=02:02:30:30:97:81//mac地址
	da850-panel=TL070A //显示设备
	da850-emac=mii //网络模式为MII
	root=ubi0_0 ubi.mtd=4,2048 //文件系统节点、分区及分区偏移量
	rootfstype=ubifs//文件系统类型为ubifs
	*/

	/*解析早期格式的内核参数*/
	parse_early_param();
	parse_args("Booting kernel", static_command_line, __start___param,
		   __stop___param - __start___param,
		   &unknown_bootoption);

	jump_label_init();

	/*
	 * These use large bootmem allocations and must precede
	 * kmem_cache_init()
	 */
	 /*初始化hash表,以便于从进程的PID获得相应的进程描述指针,按照开发板上的物理
	 内存初始化pid hash表*/
	setup_log_buf(0);
	pidhash_init();
	
	/*建立节点哈希表和数据缓冲哈希表*/
	vfs_caches_init_early();
	
	/*对异常处理函数进行排序*/
	sort_main_extable();

	/*完成对系统保留中断向量(异常、非屏蔽中断以及系统调用)的初始化*/
	trap_init();
	
	/*建立内核的内存分配器*/
	mm_init();

	/*
	 * Set up the scheduler prior starting any interrupts (such as the
	 * timer interrupt). Full topology setup happens at smp_init()
	 * time - but meanwhile we still have a functioning scheduler.
	 */
	 /*先初始化0进程,包括段选择符,描述符GDT,LDT等。然后将其他63个进程的段选择符
	 描述符置空,设置好后,将任务0的段选择符,描述符GDT,LDT等加载仅某个寄存器中。
	 接着设置系统中断定时器,中断函数判断是否要切换,意思就是当进程时间片已经消耗完时,
	 调用定时器中断函数判断是否要切换,最后定义系统调用*/
	sched_init();
	
	/*
	 * Disable preemption - early bootup scheduling is extremely
	 * fragile until we cpu_idle() for the first time.
	 */
	/*禁止内核抢占*/
	preempt_disable();
	
	/*检查中断是否已经打开,如果已经打开,则关闭中断*/
	if (!irqs_disabled()) {
		printk(KERN_WARNING "start_kernel(): bug: interrupts were "
				"enabled *very* early, fixing it\n");
		local_irq_disable();
	}
	/*创建idr缓冲区*/
	idr_init_cache();
	perf_event_init();
	
	/*互斥访问机制*/
	rcu_init();
	radix_tree_init();
	/* init some links before init_ISA_irqs() */
	early_irq_init();
	
	/*中断向量初始化*/
	init_IRQ();
	
	/*初始化优先级数组*/
	prio_tree_init();
	
	/*定时器初始化*/
	init_timers();
	
	/*高精度时钟初始化*/
	hrtimers_init();
	
	/*软中断初始化*/
	softirq_init();
	
	/*初始化资源和普通计时器*/
	timekeeping_init();
	
	/*初始化系统时钟源*/
	time_init();
	
	/*对内核的一个性能测试工具profile进行初始化*/
	profile_init();
	call_function_init();
	if (!irqs_disabled())
		printk(KERN_CRIT "start_kernel(): bug: interrupts were "
				 "enabled early\n");
	early_boot_irqs_disabled = false;
	
	/*使能中断*/
	local_irq_enable();

	/* Interrupts are enabled now so all GFP allocations are safe. */
	gfp_allowed_mask = __GFP_BITS_MASK;
	
	/*kmem_cache_init_late的目的在于完善slab分配器的缓存机制*/
	kmem_cache_init_late();

	/*
	 * HACK ALERT! This is early. We're enabling the console before
	 * we've done PCI setups etc, and console_init() must be aware of
	 * this. But we do want output early, in case something goes wrong.
	 */
	 /*初始化控制台以显示printk的内容,在此之前调用的printk只是把数据存到缓冲区*/
	console_init();
	if (panic_later)
		panic(panic_later, panic_param);
	/*如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做*/
	lockdep_info();

	/*
	 * Need to run this when irqs are enabled, because it wants
	 * to self-test [hard/soft]-irqs on/off lock inversion bugs
	 * too:
	 */
	locking_selftest();

#ifdef CONFIG_BLK_DEV_INITRD
	if (initrd_start && !initrd_below_start_ok &&
	    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
		printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
		    "disabling it.\n",
		    page_to_pfn(virt_to_page((void *)initrd_start)),
		    min_low_pfn);
		initrd_start = 0;
	}
#endif
	page_cgroup_init();
	debug_objects_mem_init();
	kmemleak_init();
	setup_per_cpu_pageset();
	numa_policy_init();
	if (late_time_init)
		late_time_init();
	sched_clock_init();
	/*校准延时函数的精确度*/
	calibrate_delay();
	pidmap_init();

	/*匿名虚拟内存域(anonymous VMA)初始化*/
	anon_vma_init();
#ifdef CONFIG_X86
	if (efi_enabled)
		efi_enter_virtual_mode();
#endif
	thread_info_cache_init();//获取thread_info缓存空间,大部分架构为空函数(包括arm)
	cred_init();//任务信用系统初始化
	fork_init(totalram_pages);//进程创建机制初始化
	proc_caches_init();//初始化进程创建机制所需的其他数据结构,为其申请空间
	buffer_init();//缓存系统初始化,创建缓存头空间,并检查其大小限时
	key_init();//内核密钥管理系统初始化
	security_init();//内核安全框架初始化
	dbg_late_init();
	vfs_caches_init(totalram_pages);//虚拟文件系统缓存初始化
	signals_init();//信号管理系统初始化
	/* rootfs populating might need page-writeback */
	page_writeback_init();//页写回机制初始化
#ifdef CONFIG_PROC_FS
	proc_root_init();//proc文件系统初始化
#endif
	cgroup_init();//control group正式初始化
	cpuset_init();//CPUSET初始化
	taskstats_init_early();//任务状态早期初始化函数,为结构体获取高速缓存,并初始化互斥机制
	delayacct_init();//任务延迟初始化

	check_bugs();//检查CPU BUG的函数,通过软件规避BUG
	
	/*ACPI-Advanced Configuration and Power Interface 高级配置及电源接口*/
	acpi_early_init(); /* before LAPIC and SMP init */
	sfi_init_late();

	ftrace_init();

	/* Do the rest non-__init'ed, we're now alive */
	rest_init();//虽然从名字来说是剩余的初始化,但是这个函数中的初始化包括了很多的内容
}


猜你喜欢

转载自blog.csdn.net/qq_40788950/article/details/84503373