疯狂内核之系统初始化
文章平均质量分 82
yunsongice
这个作者很懒,什么都没留下…
展开
-
内核映像的形成——寻找第一个目标
2.2.2寻找第一个目标<br />在make menucofig之后,即编译内核的第二步,是执行make命令(2.6之前的内核是执行make bzImage)。我们知道,make命令没有任何参数的时候,默认去执行当前目录下的Makefile文件。我们编译内核的时候是在刚刚从kernel.org下载来的linux-2.6.34.1源码包,所以执行的就是linux-2.6.34.1/Makefile文件,而这个文件的突破口,是找到它的第一个目标。下面,我们就来详细分析这个文件。<br /> <br />首先是原创 2010-12-29 22:33:00 · 4124 阅读 · 2 评论 -
内核映像的形成——链接vmlinux
2.2.5链接vmlinux<br />当我们前面$(vmlinux-dirs)目标的工作做完后,也就是形成了各目录中的built-in.o文件,那么就会回到$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds)所对应的目标中,这个目标是一行空命令,看到699行:<br />699 vmlinux-init := $(head-y) $(init-y)<br />700 vmlinux-main := $(core-y) $(libs-y) $(driv原创 2010-12-29 22:44:00 · 6384 阅读 · 0 评论 -
内核映像的形成——编译配置
2.2 内核编译分析<br />关于Kbuild的预备知识先说到这里,有了上面的这些知识大家就可以上手了,其他的那些重要的知识主要涉及到一些细节,等我们遇到了再去查GNU Make手册和linux-2.6.34.1/Documentation/kbuild下的那些文档(注意,这是一种很重要的学习方法)。<br /> <br />在递归访问目录之前,顶层Makefile要完成设置环境变量以及递归访问的准备工作。顶层Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含着针对某一特原创 2010-12-29 22:28:00 · 4412 阅读 · 1 评论 -
内核映像的形成 —— KBuild体系(三)
2.1.3 编译标志<br /><br />KBuild体系还有一个重要的概念,那就是编译标志。我们可以在一些Makefile中看到如下标志:<br />EXTRA_CFLAGS、 EXTRA_AFLAGS、 EXTRA_LDFLAGS、EXTRA_ARFLAGS<br /> <br />这些EXTRA_开头的大写字母变量都是编译标志,所有的 EXTRA_ 变量只在所定义的Kbuild Makefile中起作用。EXTRA_ 变量可以在Kbuild Makefile中所有命令中使用。<br /> <br /原创 2010-11-30 22:44:00 · 5417 阅读 · 0 评论 -
内核映像的形成 —— KBuild体系(二)
2.1.2 主机程序<br /><br />在内核编译阶段,Kbuild 要先去编译那些将在编译阶段使用的可执行文件。为了使用该可执行文件,要将编译分成二个阶段:<br />第一阶段是告诉Kbuild存在哪些可执行文件。这是通过变量 hostprogs-y来完成的。<br />第二阶段是添加一个对可执行文件的显性依赖。有两种方法:增加依赖关系到一个规则中,或是利用变量 $(always)。以下是详细叙述:<br /> <br />(1)简单的主机程序<br /> <br />在编译内核时,有时会需要编译并运原创 2010-11-30 22:41:00 · 3972 阅读 · 0 评论 -
内核映像的形成 —— KBuild体系(一)
2.1 KBuild体系<br /><br />从Linux内核2.6开始,Linux内核的编译采用Kbuild系统,这和过去的编译系统有很大的不同,尤其对于Linux内核模块的编译。在新的系统下,Linux编译系统会两次扫描Linux的Makefile:首先编译系统会读取Linux内核顶层的Makefile,然后根据读到的内容第二次读取Kbuild的Makefile来编译Linux内核。<br /> <br />Kbuild是建立在GUN make机制上的一种编译体系,理解KBuild体系,首先要理解GU原创 2010-11-30 22:36:00 · 6754 阅读 · 0 评论 -
内核映像的形成 —— MakeFile预备知识(五)
2.1.5 隐含规则<br />如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make 会试图去自动推导产生这个目标的规则和命令,如果make 可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。当然,隐含规则是make 事先约定好的一些东西。例如,我们有下面的一个Makefile:<br />foo : foo.o bar.o<br />cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)<br /> <br />我原创 2010-11-29 23:03:00 · 2103 阅读 · 0 评论 -
内核映像的形成 —— MakeFile预备知识(四)
2.1.4 函数<br />在 Makefile 中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具有智能。make 所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函数的返回值可以当做变量来使用。<br /> <br />函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:<br />$(<function> <arguments>)<br />或是<br />${<function> <arguments>}<br /> <br />这里,<function>就是函数原创 2010-11-29 23:01:00 · 2175 阅读 · 0 评论 -
内核映像的形成 —— MakeFile预备知识(三)
2.1.3 条件判断<br />使用条件判断,可以让 make 根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是变量和常量的值。<br /> <br />下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU 函数编译目标。<br />libs_for_gcc = -lgnu<br />normal_libs =<br />foo: $(objects)<br />ifeq ($(CC),gcc)<br />$(CC) -o foo $(objects) $(lib原创 2010-11-29 22:58:00 · 2367 阅读 · 0 评论 -
内核映像的形成 —— MakeFile预备知识(二)
2.1.2 Makefile变量<br />在 Makefile 中定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile 中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile 中改变其值。在Makefile 中,变量可以使用在“目标”,“依赖目标”,“命令”或是Makefile 的其它部分中。<br /> <br />变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有“:”、“#”、“=”或是空字符(空格、回原创 2010-11-29 00:58:00 · 2734 阅读 · 0 评论 -
内核映像的形成 —— MakeFile预备知识(一)
2 内核映像的形成<br />前面提到了,grub载入内核vmlinuz并解压缩到指定位置。别看这小小一句话,里边的学问可大着哩,要搞懂这里面的内幕,我们还要从内核编译说起。<br /> <br />我们知道,kernel主要是由C语言和汇编语言交叉编译而形成的,其源代码来自网上,网址为www.kernel.org。当你随便下一个内核版本并解压后,就可以看到里面的文件,要么是C代码(.c或.h文件),要么就是汇编代码(.S文件)。这些文件都是内核代码的源文件,如果只有这些源文件的话,你没法把它拿到机器上去运原创 2010-11-29 00:56:00 · 4067 阅读 · 1 评论 -
系统初始化 —— 内核引导程序
<br />前面说了,内核引导程序bootloader有很多版本,最流行的是grub和lilo。不管什么bootloader,其引导过程主要分两个阶段:主引导程序(MBR);活动分区引导记录中的次引导程序。<br /> <br />主引导程序是一个块(扇区),所以是512字节,它包含446字节的程序代码和64字节的分区表,最后还有2个字节,固定是0xAA55,用于检测MBR是否有效。<br /> <br />其中的程序代码执行扫描分区表,寻址活动分区,将位于活动分区的引导记录中的次引导程序加载到内存中并执行原创 2010-11-29 00:46:00 · 3494 阅读 · 2 评论 -
内核映像的形成——递归编译各对象
2.2.4递归编译各对象<br />当prepare和scripts目标执行完后,就会去执行$(vmlinux-dirs)目标的命令:<br />883 $(vmlinux-dirs): prepare scripts<br />884 $(Q)$(MAKE) $(build)=$@<br />885 ifdef CONFIG_MODULES<br />886 $(Q)$(MAKE) $(modbuiltin)=$@<br />887 endif<br /> <br />别看这小原创 2010-12-29 22:40:00 · 5153 阅读 · 1 评论 -
实模式汇编代码header.S——准备实模式下C语言环境
3.2.3准备实模式下C语言环境<br />好了,执行到setup代码了,header.S几乎所有的代码都在准备实模式下的C语言环境。在讲解这部分代码之前先回顾一下C语言程序关于程序中.text,.data,.bss等段的说明。由于历史原因,C程序一直由下列几部分组成:<br /> <br />1、正文段。这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是经常执行的程序(如文本编辑程序、C编译程序、shell等)在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修原创 2010-12-31 21:06:00 · 4005 阅读 · 0 评论 -
设置键盘属性
3.3.6设置键盘属性<br />所以回到main函数中,继续走,下一个该执行keyboard_set_repeat()函数了。<br /> <br /> 59/*<br /> 60 * Set the keyboard repeat rate to maximum. Unclear why this<br /> 61 * is done here; this might be possible to kill off as stale code.<br /> 62 */<br /> 63sta原创 2010-12-31 21:40:00 · 2692 阅读 · 0 评论 -
初始化堆
3.3.2初始化堆<br />回到main函数,第二个函数,init_heap(),用来检查内核初始化阶段使用的堆:<br /> <br />109static void init_heap(void)<br /> 110{<br /> 111 char *stack_end;<br /> 112<br /> 113 if (boot_params.hdr.loadflags & CAN_USE_HEAP) {<br /> 114 asm("leal原创 2010-12-31 21:19:00 · 3466 阅读 · 0 评论 -
复制初始化头变量
3.3.1复制初始化头变量<br />131行是第一个函数,copy_boot_params(),顾名思义用来拷贝启动参数到内存的0号页面中:<br /> <br /> 29static void copy_boot_params(void)<br /> 30{<br /> 31 struct old_cmdline {<br /> 32 u16 cl_magic;<br /> 33 u16 cl_offset;<br />原创 2010-12-31 21:16:00 · 3564 阅读 · 0 评论 -
实模式汇编代码header.S——头变量hdr
3.2.2初始化头变量hdr<br />在讲解“中世纪时代”的代码之前,先详细介绍一个在初始化当中非常重要的内容,hdr,全称叫做“setup_header”。这个头变量存放着所有初始化期间使用到的数据,在编译setup.bin的时候存放在.header段中,其代码我们看,从arch/x86/boot/header.S的第96行开始:<br /> <br /> 94 .section ".header", "a"<br /> 95 .globl hdr<br /> 96h原创 2010-12-31 21:02:00 · 3302 阅读 · 0 评论 -
内核映像内存布局
3 实模式下的内核代码<br />通过上一章节,大家都看到了,最终每个Linux系统都有一个类似/boot/vmlinuz-2.6.x的文件,就是所谓的内核映像。而大家对从源代码到内核映像的形成有了一个较完整的认识。前面也提到了,系统启动的时候,grub会把内核映像加载到内存,然后执行它。总结一下Grub的这一个过程,就是:<br />1. 调用一个BIOS过程显示“Loading”信息。<br />2. 调用一个BIOS过程从磁盘装入内核映像的初始部分,即将内核映像的第一个512字原创 2010-12-31 20:55:00 · 4278 阅读 · 0 评论 -
初始化中断描述符表
4.2.5 初始化中断描述符表<br />在完成了最重要和最核心的分段、分页环境的初始化后,还有一个就是初始化中断描述符idt的工作。<br /> <br /> 341/*<br /> 342 * Initialize eflags. Some BIOS's leave bits like NT set. This would<br /> 343 * confuse the debugger if this code is traced.<br /> 344 * XXX - best to initia原创 2010-12-31 22:36:00 · 2633 阅读 · 0 评论 -
解压缩内核
4.2.2解压缩内核解压缩内核使用的是decompress_kernel函数,来自arch/x86/boot/compressed/misc.c:301asmlinkage void decompress_kernel(void *rmode, memptr heap, 302 unsigned char *input_data, 303 unsigned long input原创 2010-12-31 22:24:00 · 3045 阅读 · 1 评论 -
设置视频(Video)模式
<br />接下来main将执行进入保护模式前最重要的一个步骤,设置视频模式,调用set_video()函数,来自linux/arch/x86/boot/video.c:<br /> <br />315void set_video(void)<br /> 316{<br /> 317 u16 mode = boot_params.hdr.vid_mode;<br /> 318<br /> 319 RESET_HEAP();<br /> 320<br /> 321 s原创 2010-12-31 21:45:00 · 5440 阅读 · 0 评论 -
内存的检测
3.3.5内存的检测<br />好啦,回到main函数继续我们的旅程,147行detect_memory(),用于探测物理内存的布局。注意哈,这里是第一次出现与内存管理相关的代码。<br /> <br />122int detect_memory(void)<br /> 123{<br /> 124 int err = -1;<br /> 125<br /> 126 if (detect_memory_e820() > 0)<br /> 127 er原创 2010-12-31 21:32:00 · 2390 阅读 · 0 评论 -
实模式汇编代码header.S——无用的bootsect
3.2 实模式汇编代码header.S<br />如上所述,第一和第二部分是实模式代码,这些代码来自于汇编程序arch/x86/boot/header.S和c程序arch/x86/boot/main.c。<br /> <br />再从加载开始说,如上所述,vmlinuz保护模式的代码加载到0x100000开始的位置。而实模式的代码,因为被加载的位置不要求是固定的,也就是上面文档中看到的:<br /> Kernel setup | T原创 2010-12-31 20:59:00 · 3490 阅读 · 0 评论 -
初始化0号进程
<br />arch/x86/kernel/head_32.S的336行,进入分页后的内核代码段,执行lss stack_start,%esp指令,立即为进程0建立内核态堆栈。stack_start定义在657行:<br />657 ENTRY(stack_start)<br />658 .long init_thread_union+THREAD_SIZE<br />659 .long __BOOT_DS<br /> <br />我们看到内核态堆栈由init_thread_un原创 2011-01-04 10:41:00 · 3853 阅读 · 0 评论 -
启动x86虚拟机
4.2.3 启动x86虚拟机<br />看到arch/x86/kernel/head_32.S的681行:<br />681 #include "../../x86/xen/xen-head.S"<br /> <br />这时候执行arch/x86/xen/xen-head.S下的代码:<br /> <br /> 19#ifdef CONFIG_X86_32<br /> 20 mov %esi,xen_start_info<br /> 21 mov $init_thread原创 2010-12-31 22:47:00 · 2770 阅读 · 0 评论 -
第三次启动保护模式
4.2.2 初始化GDT那么,这个gdt是在哪里初始化的呢,请看arch/x86/kernel/cpu/ common.c文件:86DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {87#ifdef CONFIG_X86_6488 /*89 * We need valid kernel segments for data and code in long mode too90 * IR原创 2010-12-31 22:44:00 · 3065 阅读 · 0 评论 -
32位x86保护模式代码
4 保护模式下的内核代码4.1 32位x86保护模式代码到保护模式的代码了,最先执行的代码就是arch/x86/boot/compressed/head_32.S中的startup_32,对于bzImage,grub把它加载到0x100000的位置。首先,来到34行ENTRY(startup_32),在这里,第一条保护模式的指令开始了。注意,前面的set_gdt仅仅是为了进入保护模式后的ljmp,意义并不大。 34ENTRY(startup_32) 35 cld 36 /*原创 2010-12-31 22:18:00 · 3415 阅读 · 1 评论 -
设置BIOS的x86模式
3.3.4设置BIOS的x86模式<br />继续走,下一个函数,set_bios_mode(),跟main来自同一个文件:<br /> <br /> 94/*<br /> 95 * Tell the BIOS what CPU mode we intend to run in.<br /> 96 */<br /> 97static void set_bios_mode(void)<br /> 98{<br /> 99#ifdef CONFIG_I386<br /> 100 str原创 2010-12-31 21:24:00 · 2994 阅读 · 0 评论 -
实模式代码main函数
3.3 实模式代码main函数<br />前面header.S中最后跳到main函数,在boot/main.c,void main()。再次强调一下,这一段C代码还是系统处于实模式下所执行的代码:<br /> <br />128void main(void)<br /> 129{<br /> 130 /* First, copy the boot header into the "zeropage" */<br /> 131 copy_boot_params();<br /> 1原创 2010-12-31 21:14:00 · 2631 阅读 · 4 评论 -
系统初始化 —— 上电
读内核源代码是一件很有意思的事。它像一条线,把操作系统,编译原理,汇编语言,计算机组成原理,C 语言,数据结构与算法,计算机系统结构等等计算机的基础课程串起来。而分析linux的启动很重要,因为牵涉到硬件的初始化和内核各模块初始化环境的搭建,所以我们就针对linux2.6.34.1/arch/x86下的代码,对从打开PC电源到屏幕上出现shell环境,来对整个Linux的初始化过程进行一个全方位的分析,希望能有帮助,不足之处,还请各位网友不吝赐教。原创 2010-11-29 00:24:00 · 5030 阅读 · 3 评论