linux-insides是github中一个文章库,里面是一系列介绍linux内核及其内在机理的文章。作者是0xAX,外国大神。所以文章都是英文的。
很幸运的是,同样在github上有一个linux-insides-zh库,是针对linux-insides的中文翻译。该工程由MintCN发起,并由很多志愿者完成。在此表示感谢。
在阅读linux-insides及linux-insides-zh时,发现内容很详尽,几乎每一行代码都有相关的解释和引申。所以,就会由很多细节在里面。这就导致细节太多,而造成对整体的框架理解的凌乱。
所以,本文的作用就是按linux-insides-zh和linux-insides的思路整理出基本的思路,而省略详尽的细节。这样可以更快的理解linux内核及其工作原理。如果某些不明白之处可以再翻阅linux-insides-zh和linux-insides理解具体细节。
对于一台PC而言,
IP 0xfff0
CS selector 0xf000
CS base 0xffff0000
CPU最开始时是运行在实模式下的。
在实模式下,结合上面CS寄存器的初始化值,可知,在CPU启动后,执行第一条指令的地址就是CS base + IP
,也就是0xfffffff0
,该地址指向4G-16Byte
的地方,称为复位向量(Reset vector)。
那么此时问题就来了,此时,系统刚供电,内存控制器还没初始化呢,到哪里去寻址呢?
问题的答案是这样的,此时,对于CPU的指令的解码工作是由南桥而不是北桥完成的,CPU的地址会被传递到南桥,并由南桥的FHW(Fireware Hub 固件集线器)解码到BIOS ROM芯片1。
所以,
jump
指令,指向BIOS运行的入口点。对于linux而言,有多个引导程序可选,如grub、syslinux等。就目前而言,linux使用最多的是GRUB 2,就以GRUB 2为例说明,GRUB2引导内核分为三个阶段:
__start
函数。_start
前面还有一些代码,是远古linux自带的bootloader。main函数首先调用copy_boot_param(void)
,主要作用内核设置信息hdr(就是上面BIOS和内核设置部分填充的setup header)。拷贝到boot_params结构中。
然后是控制台初始化console_init()
之后是堆栈初始化init_heap()
检查CPU类型validate_cpu()
,法检查CPU级别以确定系统是否能够在当前的CPU上运行。
内存分布侦测detect_memory()
,以得到系统当前内存的使用分布。
键盘初始化keyboard_init()
。
系统参数查询query_mca()
。
显示模式初始化set_video
。
进入保护模式前最后的准备工作go_to_protected_mode
。
切换到保护模式protected_mode_jump
,函数的最后跳转到32位程序入口
jmpl *%eax /*32位入口点的地址:0x10000*/
而0x10000位置的代码就是arch/x86/boot/compressed/head_64.S文件中的startup_32。
跳转到startup_64函数。
跳转到decompress_kernel函数,解压缩内核并放入到适当的内存地址中。
跳转到内存中的内核代码中。