1、Linux内核源代码导读,哈尔滨工业大学(威海) 嵌入式系统实验室 Autumn 2011,系统引导和初始化,概述 史前时代:BIOS 远古时代:引导装入程序 中世纪:setup( )函数 文艺复兴时期:startup_32( )函数 现代:start_kernel( )函数,2018/12/29,引导过程概述,在计算机刚加电的那一刻,所有的硬件设备都是毫无作用的。整个计算机内如同史前时代的大地一样,四处处于混乱,无序,随机和离散的状态,RAM中包含的是无用的随机数据。引导(bootstrap)就是从开机加电时混乱无序状态将OS映像装入RAM,并转入OS的运行,由OS管理控制计算机的过程 存储
2、OS映像的非易失介质可以是硬磁盘,软盘,EPROM或flash存储器,或网络中别的节点 以典型的硬盘引导为例,2018/12/29,引导过程概述,要使开机时从不挥发介质装入OS映像,要求CPU一开机时就能自动执行一段程序 显然,这段程序应该存储在内存的EPROM或flash中 这段程序应知道怎样从不挥发介质中装入OS映像 BIOS的一部分 各种CPU都被设计成一上电就从某个特殊的地址开始执行指令,存储在内存EPROM这个特殊位置的程序很小,不足以直接将OS映像从磁盘上读进来 磁盘的文件系统不同 OS内核映像可能经过压缩,装入的同时需要解压 不同的CPU指令也不同 ,2018/12/29,201
3、8/12/29,引导扇区,为了解决EPROM中的程序不足以直接加载OS映像,通常在磁盘的第一个扇区中存放一些映像的地理信息和一段适用于具体操作系统的程序 即为引导扇区 内容取决于具体操作系统,也可能与文件系统相关而存放在EPROM中的的程序则负责从磁盘中读入引导扇区(到内存),称之为初始引导程序 初始引导程序与具体操作系统或文件系统无关,2018/12/29,引导扇区内的程序再负责装入其他扇区,这些扇区的程序和数据共同完成整个引导过程或者由引导扇区读入一个中间的工具性程序,称为引导装入程序(boot loader),再由它负责装入OS映像 例如Linux的引导装入程序LILO 可让用户从多个操
4、作系统中有选择地引导,2018/12/29,主引导记录块 MBR,由于硬盘容量的迅速增长,一个硬盘常常被划分为若干“分区”,从而把一个物理硬盘划分为几个逻辑磁盘每个逻辑磁盘的第一个扇区仍然是引导扇区,分别用于相应的逻辑磁盘中的操作系统映像 显然,这些引导扇区已不是整个硬盘的第一个扇区了 整个硬盘第一个扇区的层次高于所有逻辑磁盘,不属于任何一个逻辑磁盘,2018/12/29,机器加电时,BIOS还是会从整个硬盘的第一个扇区开始引导,因此这个扇区称作主引导记录块MBR MBR含有硬盘分区表,和一段小程序MBR中的程序并不直接引导操作系统,而是根据盘区划分信息从一个预定的“活跃”逻辑磁盘中读入其引导
5、扇区 逻辑磁盘的引导扇区程序再负责装入OS映像 也可将LILO或GRUB放在MBR中,使引导过程少转一道弯,2018/12/29,系统启动,Bootloader第一阶段,Bootloader第二阶段,启动内核,运行init进程,用户空间,内核,LILO,GRUB等,MBR,BIOS,x86 PC上的Linux引导过程,上电/复位,2018/12/29,史前时代:BIOS,计算机加电时,由一个特殊的硬件电路在CPU的一个引脚上产生一个RESET逻辑值。RESET产生以后,就把处理器的一些寄存器设成固定的值,并执行在物理地址0xfffffff0处的代码。 此时RAM芯片中包含的是随机数据 因此必须
6、将地址0xfffffff0映射到某个只读,不挥发的存储芯片中,即ROM芯片在80x86体系中,ROM中存放的程序集叫做基本输入输出系统(Basic Input/Output System,BIOS),2018/12/29,BIOS包含几个中断驱动的低级过程 所有操作系统在启动时都要通过这些过程对计算机硬件设备初始化 一些操作系统,如MS-DOS,依赖于BIOS实现大部分系统调用,2018/12/29,注意Linux内核并不使用BIOS! 多进程,采用保护模式尤其是页式映射的现代操作系统,不适合使用BIOS的驱动 BIOS过程必须在实模式下运行,内核进入保护模式后不能与之共享函数 Linux绕开
7、了BIOS,从硬件接口和中断响应彻底地实现了自己的设备驱动层 对Linux开说,BIOS作用不过是初始引导和加电自检以及提供此过程搜集到的信息,2018/12/29,BIOS使用实地址模式,因为计算机加电启动时只有这些可以使用 一个实模式地址由一个seg段和一个off偏移量组成,计算物理地址:seg*16 + off 因此实模式下CPU寻址电路不需要全局描述符表和页表将逻辑地址转换成物理地址 显然,对GDT 、LDT和页表进行初始化的代码必须在实模式下运行,2018/12/29,BIOS的启动过程,1. 对计算机执行一系列测试,来检测现在都有什么设备以及设备是否都正常工作 这个阶段称为POST
8、(Power-On Self-Test,加电自检) 2. 初始化硬件设备 3. 搜索一个操作系统来启动 根据BIOS设置,该过程按照用户预定义的次序访问系统中的软盘、硬盘和CD-ROM的第一个扇区 4. 只要找到一个有效的设备,就把第一扇区的内容拷贝到RAM中物理地址0x00007c00开始的位置,跳转到该地址处,执行刚才装载进来的代码,2018/12/29,远古时代:引导装入程序,引导装入程序(boot loader)是由BIOS用来把操作系统内核映像装载到RAM中所调用的一个程序 由BIOS调用,不属于BIOS 如GRUB和LILO Linux使用一个引导装入程序取代硬盘第一扇区MBR中原
9、有的小程序 MBR中包含硬盘分区表和一段小程序,该程序用于装载被启动操作系统所在分区第一个扇区,2018/12/29,引导装入程序 (共446字节),硬盘分区表 (共64字节),魔数(2字节),分区1,分区2,分区3,分区4,分区标志,start CHS,分区比特,end CHS,start LBA,size,主引导记录MBR的结构示意图,2018/12/29,从磁盘启动 Linux,从磁盘启动Linux需要一个两步的引导装入程序,80x86体系下通常是Linux LOader(LILO) 还有GRUB,比LILO功能更强LILO被装在MBR上(代替那个装载活动扇区的小程序),或被装在每个磁盘
10、分区的引导扇区上 效果一样,引导装入程序被执行时,用户都可以选择装入哪个操作系统,2018/12/29,LILO的第一部分。MBR或分区引导扇区包括一个小的引导装入程序,由BIOS把这个小程序装入从地址0x00007c00开始的RAM中 第一部分的小程序执行后,便将自身搬运到地址0x00096a00,建立实模式栈,然后将LILO的第二部分装入到从地址0x00096c00开始的RAM中第二部分从磁盘读取可用操作系统映射表,并提供给用户一个提示符,让用户可以从中选择装一个操作系统,2018/12/29,用户选择了要装入的OS后,LILO就把相应分区的引导扇区拷贝到RAM中并执行它,或直接把内核映像
11、拷贝到RAM中LILO引导装入程序装入Linux内核映像要依赖于BIOS例程,主要执行如下操作: 1. 调用一个BIOS过程显示“Loading”信息 2. 调用一个BIOS过程从磁盘装入内核映像的初始部分 即将内核映像第一个512字节从地址0x00090000开始存入RAM中,将setup()函数代码从地址0x00090200开始存入RAM,2018/12/29,3. 调用一个BIOS过程从磁盘中装载其余的内核映像 使用make zImage编译的小内核映像存放在从低地址0x00010000开始处的RAM中 使用make bzImage编译的大内核映像则存放在从高地址0x00100000开始
12、处的RAM中 4. 跳转到setup( )代码,2018/12/29,中世纪:setup( )函数,setup( )汇编语言函数的代码由链接程序放在内核映像文件偏移量0x200处,引导装入程序装入内核映像时把它拷贝到从物理地址0x00090200开始的RAM中setup( )函数初始化计算机中的硬件设备,并为内核程序的执行建立环境 虽然BIOS已经初始化了大部分硬件设备,但是Linux并不依赖于BIOS,而是以自己的方式重新初始化硬件设备以增强可移植性和健壮性,2018/12/29,setup( )函数执行的操作: 1. 在AIPC兼容的系统中,它调用一个BIOS例程,以在RAM中建立系统物理
13、内存布局表 2. 设置键盘重复延时和速率 当用户一直按下一个键超过一定时间,键盘设备就反复向CPU发送相应的键盘码 3. 初始化视频卡 4. 重新初始化磁盘控制器并检测硬盘参数 5. 检查IBM微通道总线(MCA) 6. 检查PS/2指针设备(总线鼠标) 7. 检查高级电源管理(APM)BIOS的支持,2018/12/29,8. 如果BIOS支持增强磁盘驱动服务,就调用相应的BIOS过程在RAM中建立系统可用硬盘表 9. 如果内核映像被低装载到RAM中(0x00010000处),就将它移动到物理地址0x00001000处 10. 置位8042键盘控制器的A20引脚(为了兼容性的必须) 11.
14、建立临时中断描述符表(IDT)和临时全局描述符表(GDT) 12. 如果需要,重置浮点单元(FPU) 13. 重新编写可编程中断控制器(PIC),以屏蔽所有中断 但应保留IRQ2,它是两个PIC间的级联中断,2018/12/29,14. 通过设置cr0状态寄存器中的PE位,将CPU从实地址模式切换到保护模式。cr0状态寄存器中的PG位被清0,因此分页机制还没有启用 15. 跳转到startup_32( )汇编语言函数,2018/12/29,文艺复兴时期:startup_32( )函数,startup_32( )函数设置一个基本的运行环境(如堆栈)后清除BSS段,调用decompress_ker
15、nel()函数来解压内核 内核映像并不是可直接执行的目标代码,而是经过压缩的zImage或bzImage 并非所有部分都是压缩过的,压缩文件头内嵌有解压自身的代码 ss,2018/12/29,setup() /arch/i386/boot/head.Sstartup_32() /arch/i386/boot/compressed/head.Sdecompress_kernel() /arch/i386/boot/compressed/misc.cstartup_32() /arch/i386/kernel/head.Sstart_kernel() /init/main.ccpu_idle() /init/main.c,2018/12/29,内核被解压到内存之后,会再调用/arch/i386/kernel/head.S中的startup_32()函数,这个新的startup_32函数会初始化页表,并启用分页机制,2018/12/29,现代:start_kernel( )函数,start_kernel( )函数完成内核的初始化工作。几乎每个内核部件都是由这个函数进行初始化的。一要的初始化有:,Thanks!,The end.,