5.4 Hello China内存管理模型

5.4.1 Hello China的内存管理模型

在Intel 32位CPU上实现目前版本的Hello China采用的是最简单的平展模式,即数据段、代码段和堆栈段相互重叠,覆盖CPU的整个线性地址空间。下面的汇编代码是代码断、数据段和堆栈段的段描述符的定义:

  gl_sysgdt:               ;;The start address of GDT.
                           ;;In order to load the mini-kernal,the sys-
                           ;;tem loader program,such as sysldrd.com(f-
                           ;;or DOS) or sysldrb(for DISK),have initia-
                           ;;lized the GDT,and make the code segment
                           ;;and data segment can address the whole 32
                           ;;bits linear address.
                           ;;After the mini-kernal loaded,the control
                           ;;transform to the OS kernal,so the kernal
                           ;;will initialize the GDT again,this initi-
                           ;;alization will make the GDT much proper.
  gl_gdt_null        dd 0  ;;The first entry of GDT must be NULL.
                     dd 0
  gl_gdt_syscode           ;;The system code segment's GDT entry.
                     dw 0xFFFF
                     dw 0x0000
                     db 0x00
                     dw 0xCF9B
                     db 0x00
gl_gdt_sysdata              ;;The system data segment's GDT entry.
                            dw 0xFFFF
                            dw 0x0000
                            db 0x00
                            dw 0xCF93
                            db 0x00
gl_gdt_sysstack             ;;The system stack segment's GDT entry.
                            dw 0xFFFF
                            dw 0x0000   ;;The stack's base address is
                                        ;;0x01000000
                            db 0x00
                            dw 0xCF93
                            db 0x00

结合IA32 CPU的段描述符的定义,可以看出,目前Hello China实现的段属性如表5-4所示。

表5-4 Hello China的段属性

这样的实现实际上是一种最简单、最通用的实现,相当于忽略了IA32的段机制。在嵌入式开发中,这种情况最为常见,因此,按照这种模型实现的操作系统可移植性要高一些。另外,这种模型符合C语言的内存管理模型,因为按照C语言的标准,一个指针应该能够寻址地址空间中的任何对象,若采用不重合的段模型则可能会出现问题。比如有下列两个函数:

VOID Function1(DWORD* lpdwResult,DWORD dw1,DWORD dw2)
{
    *lpdwResult=dw1+dw2;
}
VOID Function2()
{
    DWORD dw1=100;
    DWORD dw2=200;
    DWORD dwResult=0;
    Function1(&dwResult,dw1,dw2);
  }

在第二个函数中,dwResult的位置实际上是在堆栈段里面的,这样在调用第一个函数(Function1)的时候,传递过去的参数(&dwResult)实际上是堆栈段的一个地址(偏移)。但是在第一个函数中引用lpdwResult时,缺省情况下是按照数据段内的地址来引用的。这样若堆栈段和数据段不重叠,就不会引用到正确的位置,导致执行结果不正确,严重的时候,还会引起系统崩溃。之所以产生这个问题,是因为一般的编译器在传递指针参数时只传递段偏移部分,而不传递段选择子。

在当前的实现中,Hello China没有实现进程,只实现了线程,而且实现时,所用的线程和操作系统核心代码以及数据共享同一线性空间。这样的实现方式,也是大多数嵌入式操作系统实现的方式。这种实现方式效率会比进程模型高,因为在线程切换的时候,没有必要切换段寄存器(这会引起整个CPU Cache的刷新),只需要完成堆栈、通用寄存器的切换即可。但也有一个弊端,就是保护功能稍微弱一些,一个线程的崩溃可能会导致整个系统的崩溃。

虽然没有充分采用IA32 CPU的分段机制,但目前Hello China的实现却充分采用了CPU的分页机制来完成内存保护功能。通过分页功能可以很容易地把线性地址空间内的内存地址,映射到物理内存当中,而且还可以实现按需内存分配功能,保证了内存资源的充分利用。

下面,我们首先对操作系统启动后的内存布局进行描述,然后对系统中的下列两个物理内存区域进行描述:

(1)核心内存池,供操作系统和设备驱动程序使用,进一步分成4KB区(以4KB为单位进行分配)和任意尺寸区(以任何尺寸进行分配);

(2)分页管理区,供应用程序使用,以分页的方式进行管理。

上述两个区域都是物理内存区域,在完成上面两个区域的管理方式的描述后,将详细介绍Hello China的虚拟内存实现方法。在当前版本中,虚拟内存的实现是建立在CPU的分页机制上的。