linux内核之存储管理二

来源:互联网 发布:打击网络赌球 编辑:程序博客网 时间:2024/05/17 08:46
  
几个重要的数据结构和函数
从硬件的角度来说,Linux内核只要能为硬件准备好页面目录PGD、页面表PT以及全局段描述表GDT和局部描述表LDT,并正确设置有关的寄存器,就完成了内存管理机制中地址映射部分的准备工作。虽然最终的目的是地址映射,但是实际上内核所需要做的管理工作却要复杂得多。在与内存管理有关的内核代码中,有几个数据结构是很重要的,这些数据结构及其使用构成了代码中内存管理的基本框架。
页面目录PGD、中间目录PMD和页面表PT分别是由表项pgd_tpmd_t、以及pte_t构成的数组,而这些表项又都是数据结构。
内核中有个全局量mem_map,是一个指针,指向一个page数据结构的数组,每个page数据结构代表一个物理页面,真个数组就代表着系统中的全部物理页面。因此,页面表项的高20位对于软件和MMU硬件又着不同的意义。对于软件,这是一个物理页面的序号,将这个序号用做下标就可以从mem_map找到代表这个物理页面的page数据结构。对于硬件,则(在低位补上120后)就是物理页面的起始地址。还有一个常用的宏操作set_pte(),用来把一个表项的值设置到一个页面表项中。
在映射的过程中,MMU首先检查的是P标志位,就是_PAGE_PRESENT,它指示着所映射的页面是否在内存中。只有在P标志位为1的时候MMU才会完成映射的全过程;否则就会因不能完成映射而产生一次缺页异常,此时表项中的其他内容对MMU就没有什么意义了。除MMU硬件根据页面表项的内容进行页面映射外,软件页可以设置或检测页面表项的内容,上面的set_pte()就是用来设置页面表项。内核中还为检测页面表项的内容定义了一些工具性的函数或宏操作。
对软件来说,页面表项为0表示尚未为这个表项(所代表的虚存页面)建立映射,所以还是空白;而如果页面表项不为0,但P标志位为0,则表示映射以及建立,但是所映射的物理页面不在内存中(已经换出到交换设备上)。
系统中的每个物理页面都又一个page结构(或mem_map_t)。系统在初始化时根据物理内存的大小建立起一个page结构数组mem_map,作为物理页面的“仓库”,里面的每个page数据结构都代表着系统中的一个物理页面。每个物理页面的page结构在这个数组离的下标就是该物理页面的序号。“仓库”里的物理页面划分成ZONE_HIGHMEM,ZONE_NORMA两个管理区(根据系统配置还可以有第三个管理区ZONE_HIGHMEM,用于物理地址超过1GB的存储空间)。
管理区ZONE_DMA里的页面是专供DMA使用的。为什么供DMA使用的页面要单独加以管理呢?首先,DMA使用的页面是磁盘I/O所必需的,如果把仓库中所有的物理页面都分配光了,那就无法进行页面与盘区的交换了。此外,还有特殊的原因。在i386CPU中,页式存储管理的硬件支持是在CPU内部实现的,而不像另有些CPU那样由一个单独的MMU提供,所以DMA不经过MMU提供的地址映射。这样,外部设备就要直接提供访问物理页面的地址,可是有些外设(特别是插在ISA总线上的外设接口卡)在这方面往往有些限制,要求用于DMA的物理地址不能过高。另一方面,正因为DMA不经过MMU提供的地址映射,当DMA所需的缓冲区超过一个物理页面的大小时,就要求两个页面在物理上连续,因为此时DMA控制器不能依靠在CPU内部的MMU将连续的虚存页面映射到物理上不连续的页面。所以,用于DMA的物理页面是要单独管理的。
每个管理区都有一个数据结构,即zone_struct数据结构。在zone_struct数据结构中有一组“空闲区间”(free_area_t)队列。为什么是“一组”队列,而不是“一个”队列呢?这也是因为常常需要成“块”地分配在物理空间内连续的多个页面,所以要按块的大小分别加以管理。因此,在管理区数据结构中既要有一个队列保持一些离散的物理页面,还要有一个队列来保持一些连续长度为2的页面块已经连续长度为4816…..、直至2MAX_ORDER的页面块。常数MAX_ORDER定义为10,也就是说最大的连续页面块可以达到2101024个页面,4M字节。
在传统的计算机结构中,整个物理空间都是均匀一致的,CPU访问这个空间中的认可一个地址所需的时间都相同,所以称为“均匀存储结构”,简称UMA。可是,在一些新的系统结构中,特别是在多CPU结构的稀土女冠中,物理春出空间在这方面的一致性却成了问题。试想有这么一种结构:
系统的中心是一条总线,例如PCT总线。
有多个CPU模块连接在系统总线上,每个CPU模块都有本地的物理内存,
但是也可以通过系统总线访问其它CPU模块的内存。
系统总线上还连接着一个共用的存储模块,所有的CPU模块都可以通过系统总线访问它。
所有这些物理内存的地址互相连续而形成一个连续的物理地址空间。
显然,就某个特定的CPU而言,访问其本地的存储器是速度最快的,而穿过系统总线访问共用存储模块或其它CPU模块上的存储器就比较慢,而且还面临因可能的竞争而引起的不确定性。也就是说,在这样的系统中,其物理存储空间虽然地址连续,“质地”却不一致,所以称为“非均质存储结构”,简称NUMA。在NUMA结构的系统中分配连续的若干物理页面时一般都要求分配在质地相同的区间(称为node,即“节点”)。
事实上,严格意义上的UMA结构几乎不存在的。就拿配置最简单的单CPUPC来说,其物理存储空间就包括RAMROM,还有图形卡上的静态RAM。但是在UMA结构中,除“主存”RAM以外的存储器都很效,所以把它们放在特殊的地址上称为小小的“孤岛”,再在编程时特别加以主页就可以了。然而,在典型的NUMA结构中就需要来自内核中内存管理机制的支持了。
由于NUMA结构的引入,对于上述的物理页面管理机制也作了相应的修正。管理区不在是属于最高层的机构,而是在每个存储节点中都有两个管理区。而且前述的page结构数组也不再是全局性的,而是从属于具体的节点了。从而,在zone_struct结构之上又有了另一层代表存储节点的pglist_data数据结构。
前面几个数据结构都是用于物理空间管理的,现在来看看虚拟空间的管理,也就是虚存页面的管理。虚存空间的管理不像物理空间的管理那样有一个总的物理页面仓库,而是以进程为基础的,每个进程都是各自的虚存(用户)空间。不过,如前所述,每个进程的“系统空间”是统一为所有进程所共享的。
如果说物理空间是从“供的角度”来管理的,也就是说:“仓库还有些什么”;则虚存空间的管理是从“需”的角度来管理的,就是“我们需要用虚存空间中的那些部分”。同时,一个进程所需要使用的虚存空间中的各个部位又未必是连续的,通常形成若干离散的虚存“区间”。很自然地,对虚存区间的抽象是一个重要的数据结构。在Linux内核中,这就vm_area_struct数据结构。在内核中用于这个数据结构的变量名常常是vma
在两种情况下虚存页面(或区间)会跟磁盘文件发生关系。一种是盘区交换(swap,当内存页面不够分配时,一些久未使用的页面可以被交换到磁盘上去,腾出物理页面以供更急需的进程使用,这就是大家所知道的一般意义上的“按需调度”页式虚存管理。另一种情况是将一个磁盘文件映射到一个进程的用户空间中。Linux提供了一个系统调用mmap()(实际上是从Unix SysVR4.2开始的),使一个进程可以将一个已经打开的文件映射到其用户空间中,此后,就可以像访问内存中的一个字符数组那样来访问这个文件的内容,而不必通过lseek()read() write()等进行文件操作。
由于虚存区间与磁盘文件的这种联系,在vm_area_struct结构中相应地设置了一些成分,用以记录和管理此种联系。
虚存空间结构中另一个重要的成分是vm_ops,这是指向一个vm_operation_struct数据结构的指针。
最后,vm_area_struct中还有一个指针vm_mm,该指针指向一个mm_struct数据结构,在内核代码中,用于这个数据结构(指针)的变量名常常是mm
显然,这是在比vm_area_struct更高层上使用的数据结构。事实上,每个进程只有一个mm_struct结构,在每个进程的“进程控制快”,即task_struct结构中,又一个指针指向该进程的mm_strcut结构。可以说,mm_strcut数据结构是进程整个用户空间的抽象,也是总的控制结构。结构中的头三个指针都是关于虚存区间的。第一个mmap用来建立一个虚存区间结构的单链线性队列。第二个mmap_avl用来建立一个虚存区间结构的AVL树。第三个指针mmap_cache,用来指向最近一次用到的那个虚存区间结构。
虽然一个进程只使用一个mm_strcut结构,反过来一个mm_struct结构却可能为多个进程共享。
在内核中经常用到这样的操作:给定一个属于某个进程的虚拟地址,要求找到其所属的区间已经相应的vma_area_struct结构。这是由find_vma()来实现。
原创粉丝点击