内核编译连接时,使用ucsimm.ld文件,形成可执行文件映象,所形成的代码段既可以使用间接寻址方式(即使用reloc段进行寻址),也可以使用绝对寻址方式。这样可以给编译器更多的优化空间。因为内核可能使用绝对寻址,所以内核加载到的内存******空间必须与ld文件中给定的内存空间完全相同。 应用程序的连接与内核连接方式不同。应用程序由内核加载(可执行文件加载器将在后面讨论),由于应用程序的ld文件给出的内存空间与应用程序实际被加载的内存位置可能不同,这样在应用程序加载的过程中需要一个重新地位的过程,即对reloc段进行修正,使得程序进行间接寻址时不至于出错。(这个问题在i386等高级处理器上方法有所不同,本文将在后面进一步分析)。 由上述讨论,至少需要两套编译连接工具。在讨论过uClinux的内存管理后本文将给出整个系统的工作流程以及系统在flash和ram中的空间分布。 coff(common object file format):一种通用的对象文件格式 elf(excutive linked file):一种为Linux系统所采用的通用文件格式,支持动态连接 flat:elf格式有很大的文件头,flat文件对文件头和一些段信息做了简化 uClinux系统使用flat可执行文件格式,gcc的编译器不能直接形成这种文件格式,但是可以形成coff或elf格式的可执行文件,这两种文件需要coff2flt或elf2flt工具进行格式转化,形成flat文件。 当用户执行一个应用时,内核的执行文件加载器将对flat文件进行进一步处理,主要是对reloc段进行修正(可执行文件加载器的详见fs/binfmt_flat.c)。以下对reloc段进一步讨论。 需要reloc段的根本原因是,程序在连接时连接器所假定的程序运行空间与实际程序加载到的内存空间不同。假如有这样一条指令: 这一条指令采用直接寻址,跳转到app_start******处执行,连接程序将在编译完成是计算出app_start的实际******(设若实际******为0x10000),这个实际******是根据ld文件计算出来(因为连接器假定该程序将被加载到由ld文件指明的内存空间)。但实际上由于内存分配的关系,操作系统在加载时无法保证程序将按ld文件加载。这时如果程序仍然跳转到绝对******0x10000处执行,通常情况这是不正确的。一个解决办法是增加一个存储空间,用于存储app_start的实际******,设若使用变量addr表示这个存储空间。则以上这句程序将改为: 增加的变量addr将在数据段中占用一个4字节的空间,连接器将app_start的绝对******存储到该变量。在可执行文件加载时,可执行文件加载器根据程序将要加载的内存空间计算出app_start在内存中的实际位置,写入addr变量。系统在实际处理是不需要知道这个变量的确切存储位置(也不可能知道),系统只要对整个reloc段进行处理就可以了(reloc段有标识,系统可以读出来)。处理很简单只需要对reloc段中存储的值统一加上一个偏置(如果加载的空间比预想的要靠前,实际上是减去一个偏移量)。偏置由实际的物理******起始值同ld文件指定的******起始值相减计算出。 这种reloc的方式部分是由uClinux的内存分配问题引起的,这一点将在uClinux内存管理分析时说明。 uClinux本身并没有关注实时问题,它并不是为了Linux的实时性而提出的。另外有一种Linux--Rt-linux关注实时问题。Rt-linux执行管理器把普通Linux的内核当成一个任务运行,同时还管理了实时进程。而非实时进程则交给普通Linux内核处理。这种方法已经应用于很多的操作系统用于增强操作系统的实时性,包括一些商用版UNIX系统,Windows NT等等。这种方法优点之一是实现简单,且实时性能容易检验。优点之二是由于非实时进程运行于标准Linux系统,同其它Linux商用版本之间保持了很大的兼容性。优点之三是可以支持硬实时时钟的应用。uClinux可以使用Rt-linux的patch,从而增强uClinux的实时性,使得uClinux可以应用于工业控制、进程控制等一些实时要求较高的应用。 |