1 / 51

第 2 章 Linux 系统的启动

第 2 章 Linux 系统的启动. 本章从系统的初始化过程介绍对 Linux 内核源码的分析的一种方法。系统的初始化流程包括:系统引导,实模式下的初始化,保护模式下的初始化共三个部分。和系统的引导过程相关的概念还有单用户模式、运行级别等。对这些概念的正确理解是配置和维护系统的必要条件。. 2.1 操作系统的启动.

morwen
Télécharger la présentation

第 2 章 Linux 系统的启动

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第2章Linux系统的启动 • 本章从系统的初始化过程介绍对Linux内核源码的分析的一种方法。系统的初始化流程包括:系统引导,实模式下的初始化,保护模式下的初始化共三个部分。和系统的引导过程相关的概念还有单用户模式、运行级别等。对这些概念的正确理解是配置和维护系统的必要条件。

  2. 2.1操作系统的启动 一般来说,操作系统的引导过程分两个步骤。首先,计算机硬件经过开机自检(Power On Self-Test,POST)之后,从软盘或硬盘的固定位置装载一小段代码,这段代码一般称为“引导装载器”。然后,由引导装载器负责装入并运行操作系统。引导装载器非常小,一般只有几百个字节,而操作系统庞大而复杂。上述分成两阶段的引导过程,可将计算机中的固化软件保持得足够小,同时也便于实现对不同操作系统的引导。

  3. 2.1.1 系统引导过程简介 系统启动过程主要由以下几个步骤组成(以硬盘启动为例): (1) 开机 (2) BIOS 加电自检 ( Power On Self Test,POST ),内存地址为 0ffff:0000 (3) 将硬盘第一个扇区 (0头0道1扇区, 也就是Boot Sector)读入内存地址 0000:7c00 处。 (4) 检查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于则转去尝试其他启动介质, 如果没有其他启动介质则显示"No ROM BASIC" 然后死机。 (5) 跳转到 0000:7c00 处执行 MBR 中的程序。 (6) MBR 首先将自己复制到 0000:0600 处, 然后继续执行。 (7) 在主分区表中搜索标志为活动的分区。 如果发现没有活动分区或有不止一个活动分区, 则停止。

  4. (8)将活动分区的第一个扇区读入内存地址0000:7c00 处。 (9)检查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于则显示 "Missing Operating System" 然后停止, 或尝试软盘启动。 (10)跳转到0000:7c00 处继续执行特定系统的启动程序。 (11)启动系统。 以上步骤中 2,3,4,5 步是由 BIOS 的引导程序完成。 6,7,8,9,10步由MBR中的引导程序完成。一般多系统引导程序 (如 Smart Boot Manager、BootStar、PQBoot 等)都是将标准主引导记录替换成自己的引导程序, 在运行系统启动程序之前让用户选择要启动的分区。而某些系统自带的多系统引导程序 (如LILO,NT Loader 等)则可以将自己的引导程序放在系统所处分区的第一个扇区中,在 Linux中即为是两个扇区的SuperBlock。 • 注:以上各步骤中使用的是标准 MBR,其他多系统引导程序的引导过程与此不同。

  5. 2.1.2 硬盘结构 1. 硬盘参数 当硬盘的容量还非常小的时候, 人们采用与软盘类似的结构生产硬盘。硬盘盘片的每一条磁道都具有相同的扇区数。 由此产生了所谓的3D参数 (Disk Geometry)以及相应的寻址方式。到目前为止,通常还是沿用这种 CHS (Cylinder/Head/Sector)来表示硬盘参数。其中:磁头数(Heads) 表示硬盘总共有几个磁头,也就是有几面盘片, 最大为 256 (用 8 个二进制位存储);柱面数(Cylinders) 表示硬盘每一面盘片上有几条磁道, 最大为 1024(用 10 个二进制位存储);扇区数(Sectors per track) 表示每一条磁道上有几个扇区, 最大为63 (用 6 个二进制位存储)。每个扇区一般是 512个字节。

  6. 2. 基本 INT 13H 调用简介 BIOS int 13H 调用是 BIOS 提供的磁盘基本输入输出中断调用, 它可以完成磁盘(包括硬盘和软盘)的复位、读写、校验、定位、诊断和格式化等功能。 它使用的是 CHS 寻址方式, 因此最大只能访问 8 GB 左右的硬盘 ( 本文中如不作特殊说明, 均以 1M = 1048576 字节为单位)。 3. 现代硬盘结构简介 在老式硬盘中, 由于每个磁道的扇区数相等, 所以外道的记录密度要远低于内道, 因此会浪费很多磁盘空间。为了进一步提高硬盘容量,人们改用等密度结构生产硬盘。 也就是说, 外圈磁道的扇区比内圈磁道多。 采用这种结构后, 硬盘不再具有实际的3D参数, 寻址方式也改为线性寻址, 即以扇区为单位进行寻址。为了与使用3D寻址的老软件兼容 (如使用BIOS INT13H接口的软件), 在硬盘控制器内部安装了一个地址翻译器, 由它负责将老式3D参数翻译成新的线性参数。不同的工作模式(如LBA、LARGE、NORMAL)对应不同的3D参数。

  7. 4. 扩展 INT 13H 虽然现代硬盘都已经采用了线性寻址,但是由于基本INT 13H 的制约,使用 BIOS 的INT13H 接口的程序(如 DOS 等)还只能访问8G以内的硬盘空间。为了打破这一限制, Microsoft 等几家公司制定了扩展 INT 13H 标准(Extended INT13H), 采用线性寻址方式存取硬盘,突破了8 G 的限制,并还加入了对可拆卸介质 (如活动硬盘) 的支持。

  8. 2.1.3 引导扇区 1. Boot Sector 的组成 Boot Sector 也就是硬盘的第一个扇区, 它由 MBR (Master Boot Record),DPT (Disk Partition Table) 和 Boot Record ID三部分组成。MBR又称作主引导记录,占用 Boot Sector 的前 446 个字节 ( 0 to 0x1BD ),存放系统主引导程序 (它负责从活动分区中装载并运行系统引导程序)。DPT 即主分区表占用 64 个字节 (0x1BE to 0x1FD), 记录了磁盘的基本分区信息。 主分区表分为四个分区项, 每项 16 字节, 分别记录了每个主分区的信息(因此最多可以有四个主分区)。Boot Record ID 即引导区标记占用两个字节 (0x1FE and 0x1FF), 对于合法引导区, 它等于 0xAA55, 这是判别引导区是否合法的标志。

  9. Boot Sector 的具体结构如下图所示:

  10. 2. 分区表结构简介 分区表由四个分区项构成, 每一项的结构如下: • BYTE State: 分区状态, 0 = 未激活, 0x80 = 激活 (注意此项) • BYTE StartHead: 分区起始磁头号 • WORD StartSC : 分区起始扇区和柱面号, 底字节的低6位为扇区号,高2位为柱面号的第 9,10 位, 高字节为柱面号的低 8 位 • BYTE Type: 分区类型, 如 0x0B = FAT32, 0x83 = Linux 等,00 表示此项未用 • BYTE EndHead: 分区结束磁头号 • WORD EndSC : 分区结束扇区和柱面号, 定义同前 • DWORD Relative : 在线性寻址方式下的分区相对扇区地址(对于基本分区即为绝对地址) • DWORD Sectors : 分区大小 (总扇区数)

  11. 在 DOS / Windows 系统下, 基本分区必须以柱面为单位划分( Sectors * Heads 个扇区), 如对于 CHS 为 764/256/63 的硬盘, 分区的最小尺寸为 256 * 63 * 512 / 1048576 = 7.875 MB。 由于硬盘的第一个扇区已经被引导扇区占用, 所以一般来说, 硬盘第一个磁道(0头0道)的其余 62 个扇区是不会被分区占用的。 某些分区软件甚至将第一个柱面全部空出来。 3. 扩展分区 由于主分区表中只能分四个分区, 无法满足需求, 因此设计了一种扩展分区格式。 基本上说, 扩展分区的信息是以链表形式存放的, 但也有一些特别的地方。

  12. 首先, 主分区表中要有一个基本扩展分区项, 所有扩展分区都隶属于它,也就是说其他所有扩展分区的空间都必须包括在这个基本扩展分区中。 对于DOS / Windows 来说, 扩展分区的类型为 0x05 或 0x0F (LBA模式)。 除基本扩展分区以外的其他所有扩展分区则以链表的形式级联存放, 后一个扩展分区的数据项记录在前一个扩展分区的分区表中, 但两个扩展分区的空间并不重叠。

  13. 扩展分区类似于一个完整的硬盘, 必须进一步分区才能使用。 但每个扩展分区中只能存在一个其他分区。 此分区在 DOS/Windows 环境中即为逻辑盘。因此每一个扩展分区的分区表 (同样存储在扩展分区的第一个扇区中)中最多只能有两个分区数据项(包括下一个扩展分区的数据项)。 以上所有扩展分区表中的第二个分区项(指向下一个扩展分区)的相对扇区地址均相对于主扩展分区, 而不是前一个扩展分区。

  14. 2.2 Linux 的引导过程 不同计算机平台引导过程的区别主要在于第一阶段的引导过程。对 PC 机上的 Linux 系统而言,计算机(即 BIOS)负责从软盘或硬盘的第一个扇区(即引导扇区)中读取引导装载器,然后,由引导装载器从磁盘或其他位置装入操作系统。从软盘引导时,BIOS 读取并运行引导扇区中的代码。引导扇区中的代码读取软盘前几百个块(依赖于实际的内核大小),然后将这些代码放置在预先定义好的内存位置。利用软盘引导 Linux 时,没有文件系统,内核处于连续的扇区中,这样安排可简化引导过程。但是,如果利用LILO(LInux LOader)也可从包含文件系统的软盘上引导 Linux。

  15. 从硬盘引导时,由于硬盘是可分区的,因此引导过程比软盘复杂一些。BIOS 首先读取并运行硬盘主引导记录中的代码,这些代码首先检验主引导记录中的分区表,寻找到活动分区(即标志为可引导分区的分区),然后读取并运行活动分区之引导扇区中的代码。活动分区引导扇区的作用和软盘引导扇区的作用一样:从分区中读取内核映象并启动内核。和软盘引导不同的是,内核映象保存在硬盘分区文件系统中,而不象软盘那样保存在后续的连续扇区中,因此,硬盘引导扇区中的代码还需要定位内核映象在文件系统中的位置,然后装载内核并启动内核。

  16. Linux系统的常见引导方式有两种:LILO引导和Loadin引导;其中LILO可实现多重引导, Loadin可在DOS下引导Linux。此外,Linux内核也自带了一个bootsect-loader。由于bootsect-loader只能实现Linux的引导,不像前两个那样具有很大的灵活性,所以在普通应用场合实际上很少使用。但由于bootsect-loader短小、没有多余的代码、并且是内核源码的有机组成部分。下面将主要对bootsect-loader文件进行分析。bootsect-loader在内和源码中对应的程序是 /arch/i386/boot/bootsect.s 。几个相关文件是: • (1) /arch/i386/boot/bootsect.s • (2) /include/linux/config.h • (3) /include/asm/boot.h • (4) /include/linux/autoconf.h

  17. 2.2.1 Linux系统引导过程分析 开启Intel x86 PC的电源后,机器就会开始执行ROM BIOS的一系列系统测试动作,包括检查RAM、键盘、显示器和软硬磁盘等。接着控制权转移给ROM中的启动程序(ROM bootstrap routine);该程序会将磁盘上的第0轨第0扇区(称为boot sector或Master Boot Record,MBR,系统的引导程序就放在此处)读入内存,放入自0x07C0:0x0000开始的512个字节处;然后处理机跳转到该处开始执行(位于该处的)MBR引导程序,CS:IP = 0x07C0:0x0000 。加电后处理机运行在与8086相兼容的实模式下。 如果要用bootsect-loader进行系统引导,则必须把bootsect.s编译连接后对应的二进制代码置于MBR; 当ROM BIOS 把bootsect.s编译连接后对应的二进制代码装入内存后,机器的控制权就完全转交给bootsect;也就是说,bootsect将是第一个被读入内存中并执行的程序。

  18. Bootsect接管机器控制权后,将依次进行以下一些动作: (1) 首先,bootsect将它“自己”(自位置0x07C0:0x0000开始的512个字节)从被ROM BIOS载入的地址0x07C0:0x0000处搬到0x9000:0000处; 这一任务由bootsect.s的前十条指令完成;第十一条指令“jmpi go,INITSEG”则把机器跳转到“新”的bootsect的“jmpi go,INITSEG”后的那条指令“go: mov di,#0x4000-12”;之后,继续执行bootsect的剩下的代码;在bootsect.s中定义了几个常量: BOOTSEG = 0x07C0 bios 载入 MBR的约定位置的段址; INITSEG = 0x9000 bootsect.s的前十条指令将自己搬到此处(段址) SETUPSEG =0x9020 装入Setup.s的段址 SYSSEG =0x1000 系统区段址 这些常量的定义可参见/include/asm/boot.h;在下面的分析中这些常量会经常用到。

  19. (2) 以0x9000:0x4000-12为栈底,建立自己的栈区;其中0x9000:0x4000-12到0x9000:0x4000的一十二个字节预留作磁盘参数表区; (3) 在0x9000:0x4000-12到0x9000:0x4000的一十二个预留字节中建立新的磁盘参数表。由于设计者考虑到有些老的bios不能准确地识别磁盘每个磁道的扇区数,使得bios建立的磁盘参数表妨碍磁盘的最高性能发挥,所以设计者在bios建立的磁盘参数表的基础上使用枚举法测试,试图建立准确的“新”的磁盘参数表(这是在后继步骤中完成的);并把参数表的位置由原来的0x0000:0x0078搬到0x9000:0x4000-12;且修改老的磁盘参数表区使之指向新的磁盘参数表;

  20. (4)接下来是load_setup子过程;它调用0x13中断的第2号服务;把第0道第2扇区开始的连续的setup_sects (为常量4)个扇区读到紧邻bootsect的内存区;,即0x9000:0x0200开始的2048个字节;而这四个扇区的内容就是/arch/i386/boot/setup.s编译连接后对应的二进制代码。换句话说,如果要用bootsect-loader进行系统引导,不仅必须把bootsect.s编译连接后对应的二进制代码置于MBR,而且还要将\把setup.s编译连接后对应的二进制代码置于紧跟MBR后的连续的四个扇区中。由于setup.s对应的可执行码是由bootsect装载的,所以可以根据需要修改bootsect来设置setup.s对应的可执行码; (5) load_setup子过程的惟一出口是probe_loop子过程;该过程通过枚举法测试磁盘“每个磁道的扇区数”;

  21. (6) 接下来几个子过程比较清晰:显示“Loading”;读入系统到0x1000:0x0000; 关掉软驱马达;根据的5步测出的“每个磁道的扇区数”确定磁盘类型;最后跳转到0x9000:0x0200,即setup.s对应的可执行码的入口,将机器控制权转交setup.s;整个bootsect代码运行完毕。引导过程执行完后的内存映像图如图2.3所示。完成了系统的引导后,系统将进入到初始化处理阶段。系统的初始化分为实模式和保护模式两部分。 为了简单起见,在上面的分析中,忽略了对大内核的处理的分析。因为对大内核的处理,只是此引导过程中的一个很小的部分,并不影响对整体的把握。

  22. 2.2.2 实模式下的初始化 实模式下的初始化,主要是指从内核引导成功后,到进入保护模式之前系统所做的一些处理。在内核源码中对应的程序是 /arch/i386/boot/setup.s;以下部分主要对该文件进行分析。这部分的分析要弄懂它的处理流程和INITSEG(9000:0000)段参数表的建立的过程。该参数表包含了很多硬件参数,这些都是以后进行保护模式下初始化以及核心建立的基础。相关文件有: /arch/i386/boot/bootsect.s /include/linux/config.h /include/asm/boot.h /include/ asm/segment.h /include/linux/version.h /include/linux/compile.h

  23. setup.s完成在实模式下版本检查,并将硬盘、鼠标及内存的参数写入到 INITSEG中,并负责进入保护模式。实模式下的初始化过程如图2.4及图2.5所示:

  24. 图2.4 实模式下的初始化过程1

  25. 图2.5 实模式下的初始化过程2

  26. 表 2.1 INITSEG(9000:0000)段参数表:(参见include/linux/tty.h)

  27. 注: ① include/linux/tty.h : CL_MAGIC and CL_OFFSET here ② include/linux/tty.h : unsigned char rsvd_size; // 0x2c unsigned char rsvd_pos; // 0x2d ③ 0表示没有电源管理(APM)BIOS ④ 0x0002置位表示支持32位模式 ⑤ 0表示没有,0x0aa表示有鼠标器

  28. 2.2.3 保护模式下的初始化 保护模式下的初始化,是指处理机进入保护模式后到运行系统第一个内核程序过程中,系统所做的一些处理。保护模式下的初始化在内核源码中对应的程序是 /arch/i386/boot/compressed/head.s 和 /arch/i386/kernel/head.s ;以下部分主要是针对这两个文件进行的分析。 在i386体系结构中,因为i386本身的问题,在"arch/alpha/kernel/head.s"中需要更多的设置,最终是通过call SYMBOL_NAME(start_kernel)转到start_kernel()这个体系结构无关的函数中去执行了。

  29. 在i386系统中,当内核以bzImage的形式压缩,即大内核方式(BIG_KERNEL)压缩时就需要预先处理bootsect.s和setup.s,按照大核模式使用$(CPP)处理生成bbootsect.s和bsetup.s,然后再编译生成相应的.o文件,并使用"arch/i386/boot/compressed/build.c"生成的build工具,将实际的内核(未压缩的,含kernel中的head.s代码)与"arch/i386/boot/compressed"下的head.s和misc.c合成到一起,其中的head.s代替了"arch/i386/kernel/head.s"的位置,由Bootloader引导执行(startup_32入口),然后它调用misc.c中定义的decompress_kernel()函数,使用"lib/inflate.c"中定义的gunzip()将内核解压到0x100000,再转到其上执行"arch/i386/kernel/head.s"中的startup_32代码。在i386系统中,当内核以bzImage的形式压缩,即大内核方式(BIG_KERNEL)压缩时就需要预先处理bootsect.s和setup.s,按照大核模式使用$(CPP)处理生成bbootsect.s和bsetup.s,然后再编译生成相应的.o文件,并使用"arch/i386/boot/compressed/build.c"生成的build工具,将实际的内核(未压缩的,含kernel中的head.s代码)与"arch/i386/boot/compressed"下的head.s和misc.c合成到一起,其中的head.s代替了"arch/i386/kernel/head.s"的位置,由Bootloader引导执行(startup_32入口),然后它调用misc.c中定义的decompress_kernel()函数,使用"lib/inflate.c"中定义的gunzip()将内核解压到0x100000,再转到其上执行"arch/i386/kernel/head.s"中的startup_32代码。

  30. 几个有关文件是: /arch/i386/boot/compressed/head.s /arch/i386/kernel/head.s //arch/i386/boot/compressed/MISC.c /arch/i386/boot/setup.s /include/ asm/segment.h /arch/i386/kernel/traps.c /include/i386/desc.h /include/asm-i386/processor.h

  31. 保护模式下的初始化过程大致如下: 1. /arch/i386/kernel/head.s流程 因为在setup.s最后的是一条转跳指令,跳到内核第一条指令并开始执行。指令中指向的是内存中的绝对地址,无法依此判断转跳到了head.s。但是可以通过Makefile简单的确定head.s位于内核的前端。 在arch/i386 的 Makefile 中定义了 HEAD := arch/i386/kernel/head.o 在Linux总的Makefile中的语句include arch/$(ARCH)/Makefile说明HEAD定义在该文件中有效。

  32. 由如下语句: vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ $(ARCHIVES) \ $(FILESYSTEMS) \ $(DRIVERS) \ $(LIBS) -o vmlinux $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map

  33. 从这个依赖关系可以获得大量的信息: (1)$(HEAD)即head.o的确第一个被连接到核心中; (2)内核支持的所有文件系统全部编译到$(FILESYSTEMS)即fs/filesystems.a中; (3)内核中支持的所有网络协议全部编译到net.a中; (4)内核中支持的所有SCSI驱动全部编译到scsi.a中; ................... 这样看来,内核就是一堆库文件和目标文件的集合。如果要对内核减肥的话,可以好好比较一下,看究竟是那个部分占用了空间。 在System.map中包含了所有的内核输出的函数,这些是在编写内核模块的时候可以调用的系统函数。

  34. 2. head.s的分析 (1) 首先将ds、es、fs和gs指向系统数据段KERNEL_DS(KERNEL_DS 在asm/segment.h中定义,表示全局描述符表中中的第三项)。 注意:该此时生效的全局描述符表并不是在head.s中定义的,而仍然是在setup.s中定义的。 (2)数据段全部清空。 (3)setup_idt为一段子程序,将中断向量表全部指向ignore_int函数。该函数打印出“unknown interrupt”,当然这样的中断处理函数什么也干不了。 (4) 查看数据线A20是否有效,否则循环等待。地址线A20是x86的历史遗留问题,决定是否能访问1M以上内存。 (5)拷贝启动参数到0x5000页的前半页,而将setup.s取出的bios参数放到后半页。 (6)检查CPU类型。

  35. (7)初始化页表,只初始化最初几页。 • ①将swapper_pg_dir(0x2000)和pg0(0x3000)清空,swapper_pg_dir作为整个系统的页目录。 • ② 将pg0作为第一个页表,将其地址赋到swapper_pg_dir的第一个32位字中。 • ③ 同时将该页表项也赋给swapper_pg_dir的第3072个入口,表示虚拟地址 0xc0000000也指向pg0。 • ④ 将pg0这个页表填满指向内存前4M。 • ⑤ 进入分页方式。 (8) 装入新的GDT和ldt表。 (9) 刷新段寄存器ds、es、fs和gs。 (10)使用系统堆栈,即预留的0x6000页面。 (11)执行start_kernel函数,这是第一个C编制的函数,内核又有了一个新的开始。

  36. 图 2.6 /arch/i386/kernel/head.s流程图一

  37. 图2.7 /arch/i386/kernel/head.s流程图二

  38. 3. /arch/i386/boot/compressed/head.s流程 图2.8是 /arch/i386/boot/compressed/head.s的流程图。从图中可看到,保护模式下的初始化主要做了几件事:解压内核到0x100000处、建立页目录和pg0页表并启动分页功能(即虚存管理功能)、保存实模式下测到的硬件信息到empty_zero_page、初始化命令缓存区、检测cpu类型、检查协处理器、 重新建立GDT全局描述符表和中断描述附表IDT。

  39. 图2.8 /arch/i386/boot/compressed/head.s流程

  40. 从页目录和pg0页表可以看出,0�4M物理内存被用作系统区,它被映射到系统段线性空间的0�4M和3G�3G+4M;即系统可以通过访问这两个段来访问实际的0�4M物理内存,也就是系统所在的区域; 本来在实模式下初始化时已经建立了全局描述符表GDT,而此处重新建立全局描述符表GDT则主要是出于两个原因:一个就是若内核是大内核bzimag,则以前建立的GDT,可能在解压时已经被覆盖了。所以在这个源码文件中均只采用相对转移指令jxx nf或jxx nb;二是以前建立的GDT是建立在实地址方式下的,而现在则是在启用保护虚拟地址方式之后建立的,也即现在的GDT是建立在逻辑地址(即线性地址)上的; 每次建立新的GDT后和启用保护虚拟地址方式后都必须重新装载系统栈和重新初始化各段寄存器:cs,ds,es,fs,gs;

  41. 从实模式下的初始化和保护模式下的初始化过程可以看出,Linux系统由实模式进入到保护模式的过程大致如图2.9所示。 由于分页机制只能在保护模式下启动,不能在实模式下启动,所以第一步是必要的;又因为在386保护模式下GDT和IDT是建立在逻辑地址(线性地址)上的,所以第三步也是必要的;

  42. 经过实模式和保护模式下的初始化后,主要系统数据的分布如表2.2所示。

  43. 从上面对Linux系统的初始化过程的分析可以看出,以程序执行流程为线索,即按照程序的执行先后顺序,弄懂程序执行的各个阶段所进行的处理,及其各阶段之间的相互联系。而流程图应该是这种分析方法最合适的表达工具。 事实上,以程序执行流程为线索,是分析任何源代码都首选的方法。用这种方法来分析系统的初始化过程或用户进程的执行流程应该说是很有效的。当然由于操作系统的特殊性,光用这种方法是远远不够的。

  44. 2.2.4 系统初始化 Linux 内核装入之后,Linux 内核进行硬件和设备驱动程序的初始化,然后运行init。init是 Linux 内核启动的第一个用户级进程,其进程标识号始终为 1,该进程在系统引导和关机过程中扮演重要角色。Linux 内核进行的初始化工作可大体描述如下: (1) Linux 内核一般是压缩保存的,因此,它首先要进行自身的解压缩。内核映象前面的一些代码完成解压缩。 (2) 如果系统中安装有可支持特殊文本模式的、且 Linux 可识别的 SVGA 卡,Linux 会提示用户选择适当的文本显示模式。但是,如果在内核的编译过程中预先设置了文本模式,则不会提示选择显示模式。该显示模式也可通过 LILO 或 rdev 设置。 (3) 内核接下来检测其他的硬件设备,例如硬盘、软盘和网卡等,并对相应的设备驱动程序进行配置。这时,内核会输出一些硬件信息,类似下面的输出:

  45. LILO boot:Loading Linux.Memory: sized by int13 088hConsole: 16 point font, 400 scansConsole: colour VGA+ 80x25, 1 virtual console (max 63)pcibios_init : BIOS32 Service Directory structure at 0x000f7510pcibios_init : BIOS32 Service Directory entry at 0xfd7d2pcibios_init : PCI BIOS revision 2.10 entry at 0xfd9e6Probing PCI hardware.Calibrating delay loop.. ok - 231.83 BogoMIPSMemory: 30760k/32704k available (748k kernel code, 384k reserved, 812k data)Swansea University Computer Society NET3.035 for Linux 2.0NET3: UNIX domain sockets 0.13 for Linux NET3.035.Swansea University Computer Society TCP/IP for NET3.034IP Protocols: IGMP, ICMP, UDP, TCPLinux IP multicast router 0.07.VFS: Diskquotas version dquot_5.6.0 initializedChecking 386/387 coupling... Ok, fpu using exception 16 error reporting.Checking 'hlt' instruction... Ok.Linux version 2.0.36 (root@porky.Red Hat.com) (gcc version 2.7.2.3) #1 Tue Oct 13 22:17:11 EDT 1998Starting kswapd v 1.4.2.2 Serial driver version 4.13 with no serial options enabledtty00 at 0x03f8 (irq = 4) is a 16550AReal Time Clock Driver v1.09Ramdisk driver initialized : 16 ramdisks of 4096K sizeide: i82371 PIIX (Triton) on PCI bus 0 function 57 ide0: BM-DMA at 0xfcd0-0xfcd7 ide1: BM-DMA at 0xfcd8-0xfcdfhda: HITACHI_DK237A-32, 3102MB w/512kB Cache, CHS=788/128/63, UDMAhdc: CD-224E, ATAPI CDROM driveide0 at 0x1f0-0x1f7,0x3f6 on irq 14ide1 at 0x170-0x177,0x376 on irq 15Floppy drive(s): fd0 is 1.44MFDC 0 is a post-1991 82077md driver 0.36.3 MAX_MD_DEV=4, MAX_REAL=8scsi : 0 hosts.scsi : detected total.Partition check: hda: hda1 hda2 < hda5 > hda3 hda4

  46. (4) 接下来,内核挂装 root 文件系统。root 文件系统的位置可在编译内核时指定,也可通过LILO或rdev指定。文件系统的类型可自动检测。如果由于某些原因挂装失败,则内核启动失败,最终会终止系统。root 文件系统一般挂装为只读文件系统。 (5)此后,内核启动init进程(位于 /sbin/init)。一般而言,init进程要启动一些重要的后台守护进程。 (6)然后, init切换到多用户模式,并为每个虚拟控制台和串行线路启动一个 getty进程,getty进程管理用户从虚拟控制台和串行终端上的登录。根据不同的配置,init也可以启动其他进程。 (7) 至此,系统的引导过程结束。

  47. 初始化进程开始执行/sbin /init之后,系统内核就不再对程序进行直接控制了。之后系统内核的作用主要是给进程提供系统调用,以及提供异步中断事件的处理。多任务机制已经建立起来,并开始处理多个用户的登录和fork( )创建的进程。 对Linux内核源码的分析,有几个很好的入口点:一个就是系统的引导和初始化,即从机器加电到系统核心的运行;对于那些对硬件比较熟悉的读者,可从系统的引导入手进行分析,根据系统的初始化过程,把系统的初始化过程所涉及到的代码分析清楚。 Linux的源码是一个组织得有条有理的蛛网。要把整个结构分析清楚,除了找出线头,还得理顺各个部分之间的关系,有条不紊的一点一点的分析。

More Related