Linux
的存储管理虚存管理
Linux
里使用了N个概念来描述虚存系统:struct mm_struct
核心为每个进程的地址空间维护一个数据结构
—struct mm_struct(linux/sched.h),每个进程的task_struct(linux/sched.h)结构中都有一个指向它的指针。该结构的成员如下:
成员名 |
用途 |
count |
mm_struct 结构的引用计数。为了在Linux中实现线程,系统调用clone派生一个线程,它具备进程所具有的控制结构,但和调用进程共享内存,即mm_struct结构,派生后系统会累加mm_struct中的引用计数。 |
pgd |
页目录基址 |
context |
虚存上下文。在有的体系结构中,虚存地址要加上一个上下文标识才能进行地址转换,如 MIPS系列,这个域就是进程的上下文标识。 |
start_code, end_code, start_data, end_data |
进程的代码和数据的起始地址。 |
start_brk, brk, start_stack, start_mmap |
进程堆的起始地址,当前地址,进程用户栈的起始地址,进程映射空间的起始地址。 |
rss, total_vm, locked_vm |
进程贮留在物理内存中的页面,进程所需的总的页面数,被锁定在物理内存中的页面数。 |
def_flags |
进程某人的页面标志。系统在设定页面标志时,首先 OR上这个标志。如在调用mlockall(MCL_CURRENT)时,系统就会将def_flags设置为VM_LOCKED,这样以后在调入的页面也被标记为VM_LOCKED的,就完成相应的页面锁定工作。 |
mmap |
struct vm_area_struct 队列,按基址从小到大排列。 |
mmap_avl |
struct vm_area_struct AVL 树。核心经常会完成从虚址到相应的struct vm_area_struct结构的转换操作,如果总是线性地查找mmap队列就会非常地耗时。利用AVL树可以将查找的代价控制在O(logN)的水平上。 |
mmap_sem |
系统修改进程的 mmap队列和mmap_avl树时用于互斥的信号量。 |
struct vm_area_struct(linux/mm.h)
进程的地址空间是由一系列的虚存区构成的。虚存区的结构成员如下
:
成员名 |
用途 |
vm_mm |
指向所属的 mm_struct结构的指针。 |
vm_start ,vm_end |
虚存区的起始地址。 |
vm_page_prot |
虚存区的保护权限。 |
vm_flags |
虚存区的标志。 |
vm_avl_height ,vm_avl_left,vm_avl_right |
这 3个成员在一起构成AVL树,其中vm_avl_height是该节点距跟节点的高度,vm_avl_left和vm_avl_right分别时该节点的左右两个子树。 |
vm_next |
构成虚存区的线性链,按基址从小到大排列。 |
vm_next_share ,vm_prev_share |
当虚存区是文件映射空间的一部分时,这两个域将所有属于同一个文件(即同一个 inode)的虚存区连接在一起。当虚存区时 SYS V共享内存的一部分时,这两个域将所有属于同一片共享内存的虚存区连接在一起。 |
vm_ops |
虚存区的函数开关表。这个表里给出了可以在对虚存区中的页面进行的操作(由 vm_operations_struct(linux/mm.h)结构署名),包括open,close,unmap,protect,sync,advise,nopage,wppage,swapout,swapin。Linux中较为常用的几个vm_operations_struct结构是shm_vm_ops(ipc/shm.c),file_shared_mmap(mm/filemap.c),file_private_mmap(mm/filemap.c),这3个变量分别对应SYS V共享内存的虚存区函数开关表,文件共享方式映射的虚存区函数开关表,文件私有式映射的虚存区函数开关表。 |
vm_offset |
虚存区在文件中的偏移。如果时 SYS V的共享内存,则是其在共享内存段中的偏移(虽然shmat完成了整个共享内存段的映射,但进程在以后的操作中可能将映射这个共享内存段的虚存区打裂,如在相应的虚存区上在映射文件。所以SYS V的共享内存区也需要这个偏移)。 |
vm_inode |
当虚存区为文件映射时,是该文件在内存中的 inode。如果时SYS V的共享内存则为空。其他情况也为空。注意,系统时靠vm_ops来区分是否为SYS V的共享内存区。 |
vm_pte |
当虚存区为 SYS V的共享内存时,这个域存放虚存区所在的共享内存段的ID(不是简单的存放ID,具体格式见程序ipc/shm.c)。 |
struct page(linux/mm.h)
struct page
时物理页面的对应数据结构(与mem_map_t相同)。其结构成员如下:
成员名 |
用途 |
next ,prev |
当页面空闲时,这两个指针是空闲页面链表的前后指针。当页面时某个 inode在内存中的贮留页面时,next和prev构成该inode在内存中页面的双向链表。 |
inode |
该页面对应的 inode。 |
offset |
该页面在由 inode指定的文件中的偏移。 |
next_hash ,prev_hash |
这两个域构成贮留在内存中的页面数据的哈希链。 |
count |
页面的引用计数。 |
flags |
页面的标志,下面详细介绍。 |
dirty |
??? |
age |
页面的年龄, age == 0时年龄最大。这个值反应了页面最近的使用情况,最近不使用的页面年龄较大,利用这个值控制页面的换入换出。 |
wait |
等待在这样页面上的任务的单向链表。 |
buffers |
当这个页面用做磁盘缓冲时,这个域是磁盘缓冲区双向循环链表。 |
swap_unlock_entry |
页面换出时要对相应换出设备上的页面上锁,有的换出有时是异步的,此时需要记住换出设备和其上的页面偏移以便解锁,这个域就是暂存这个数值的。 |
map_nr |
该结构在 mem_map数组中的索引,也就是页面在物理内存中的物理页面号。 |
mem_map
Linux
在管理物理页面时设置若干数据结构,首先时mem_map数组。这个数组的每一项都是strut page结构,对应着物理内存中的一个页面,记录它们的状态和有关其内容的元信息。系统在初始化时根据物理内存的大小建立这个数组(这段代码在mm/page_alloc.c中的free_area_init中)。空闲区
其次,为了维护连续空闲内存,系统还维护一组称为空闲区(
free_area)结构。每个空闲区对应一个固定的内存大小(2N×PAGE_SIZE,N=0,1,…,NR_MEM_LISTS-1),该结构有一个双向循环链表将指定大小的空闲区连接起来。此外,它还有一个位图,每一位对应连续的两个页面。当这两个页面都时空闲时,也就是有两次释放动作发生,这个二进制位经过两次异或又恢复为0,此时说明这两个都是空闲,应该从2N空闲区删除,并加入到2(N+1)空闲区中。所以,Linux空闲页面的管理实际上以页面为单位的伙伴系统(Buddy System)。page cache
虚存系统将全部内存或内存的一部分最为后备存储介质(文件,交互区等)的
cache。Linux中将文件贮留在内存中的部分通过一个哈希链表管理起来,这个哈希链表就是page cache。系统将页面的inode和offset最为键值,并通过find_page(mm/filemap.h)寻找相应的页面。除了
page cache外,系统中另外一个cache部件是磁盘缓冲区,这部分在文件系统中介绍,此处忽略。体系无关
MMU管理