万卷书   首页 > 电脑书籍 > Linux >Linux内核分析方法谈(上)
 
方法之二:以程序流程为线索,一线串珠

  从表面上看,Linux的源码就象一团扎乱无章的乱麻,其实它是一个组织得有条有理的蛛网。要把整个结构分析清楚,除了找出线头,还得理顺各个部分之间的关系,有条不紊的一点一点的分析。

  所谓以程序流程为线索、一线串珠,就是指根据程序的执行流程,把程序执行过程所涉及到的代码分析清楚。这种方法最典型的应用有两个:一是系统的初始化过程;二是应用程序的执行流程:从程序的装载,到运行,一直到程序的退出。

  为了简便起见,遵从循序渐进的原理,现就系统的初始化过程来具体的介绍这种方法。系统的初始化流程包括:系统引导,实模式下的初始化,保护模式下的初始化共三个部分。下面将一一介绍。

 inux系统的常见引导方式有两种:Lilo引导和Loadin引导;同时linux内核也自带了一个bootsect-loader。由于它只能实现linux的引导,不像前两个那样具有很大的灵活性(lilo可实现多重引导、loadin可在dos下引导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

引导过程分析:

对于Intel x86 PC , 开启电源后, 机器就会开始执行ROM BIOS的一系列系统测试动作,包括检查RAM,keyboard,显示器,软硬磁盘等等。执行完bios的系统测试之后,紧接着控制权会转移给ROM中的启动程序(ROM bootstrap routine);这个程序会将磁盘上的第0轨第0扇区(叫boot sector或MBR ,系统的引导程序就放在此处)读入内存中,并放到自0x07C0:0x0000开始的512个字节处;然后处理机将跳到此处开始执行这一引导程序;也即装入MBR中的引导程序后, CS:IP = 0x07C0:0x0000 。加电后处理机运行在与8086相兼容的实模式下。

如果要用bootsect-loader进行系统引导,则必须把bootsect.S编译连接后对应的二进制代码置于MBR; 当ROM BIOS 把bootsect.S编译连接后对应的二进制代码装入内存后,机器的控制权就完全转交给bootsect; 也就是说,bootsect将是第一个被读入内存中并执行的程序。

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中的定义;这些常量在下面的分析中将会经常用到;

2.以0x9000:0x4000-12为栈底,建立自己的栈区;其中0x9000:0x4000-12到0x9000:0x4000的一十二个字节预留作磁盘参数表区;

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

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子过程;该过程通过枚举法测试磁盘“每个磁道的扇区数”;

6.接下来几个子过程比较清晰易懂:打印我们熟悉的“Loading”;读入系统到0x1000:0x0000; 关掉软驱马达;根据的5步测出的“每个磁道的扇区数”确定磁盘类型;最后跳转到0x9000:0x0200,即setup.S对应的可执行码的入口,将机器控制权转交setup.S;整个bootsect代码运行完毕;

引导过程执行完后的内存印象图:


  出于简便考虑,在此分析中,我忽略了对大内核的处理的分析,因为对大内核的处理,只是此引导过程中的一个很小的部分,并不影响对整体的把握。完成了系统的引导后,系统将进入到初始化处理阶段。系统的初始化分为实模式和保护模式两部分。

上一页 下一页