自: http://hi.baidu.com/flying5/blog 不错的博客,大家可以去看看
Linux 的 mmap 文件内存映射机制
mmap: memory map
在讲述文件映射的概念时 , 不可避免的要牵涉到虚存 (SVR 4 的 VM). 实际上 , 文件映射是虚存的中心概念 , 文件
映射一方面给用户提供了一组措施 , 好似用户将文件映射到自己地址空间的某个部分 , 使用简单的内存访问指令
读写文件; 另一方面 , 它也可以用于内核的基本组织模式 , 在这种模式种 , 内核将整个地址空间视为诸如文件之类
的一组不同对象的映射 . 中的传统文件访问方式是 , 首先用 open 系统调用打开文件 , 然后使用 read, write 以及
lseek 等调用进行顺序或者随即的 I/O. 这种方式是非常低效的 , 每一次 I/O 操作都需要一次系统调用 . 另外 , 如果
若干个进程访问同一个文件 , 每个进程都要在自己的地址空间维护一个副本 , 浪费了内存空间 . 而如果能够通过
一定的机制 将页面映射到进程的地址空间中 , 也就是说 首先通过简单的产生某些内存管理数据结构完成映射的创
建. 当进程访问页面时产生一个缺页中断 , 内核将页面读入内存并且更新页表指向该页面 . 而且这种方式非常方
便于同一副本的共享 .
VM 是面向对象的方法设计的 , 这里的对象是指内存对象 : 内存对象是一个软件抽象的概念 , 它描述内存区与后
备存储之间的映射 . 系统可以使用多种类型的后备存储 , 比如交换空间 , 本地或者远程文件以及帧缓存等等 . VM
系统对它们统一处理 , 采用同一操作集操作 , 比如读取页面或者回写页面等 . 每种不同的后备存储都可以用不同
的方法实现这些操作 . 这样 , 系统定义了一套统一的接口 , 每种后备存储给出自己的实现方法 . 这样 , 进程的地址
空间就被视为一组映射到不同数据对象上的的映射组成 . 所有的有效地址就是那些映射到数据对象上的地址 . 这
些对象为映射它的页面提供了持久性的后备存储 . 映射使得用户可以直接寻址这些对象 .
值得提出的是 , VM 体系结构独立于 Unix 系统 , 所有的 Unix 系统语义 , 如正文 , 数据及堆栈区都可以建构在基本
VM 系统之上 . 同时 , VM 体系结构也是独立于存储管理的 , 存储管理是由操作系统实施的 , 如: 究竟采取什么样的
对换和请求调页算法 , 究竟是采取分段还是分页机制进行存储管理 , 究竟是如何将虚拟地址转换成为物理地址等
等(Linux 中是一种叫 Three Level Page Table 的机制 ), 这些都与内存对象的概念无关 .
下面介绍 Linux 中 VM 的实现 .
一个进程应该包括一个 mm_struct(memory manage struct), 该结构是进程虚拟地址空间的抽象描述 , 里面包
括了进程虚拟空间的一些管理信息 : start_code, end_code, start_data, end_data, start_brk, end_brk 等等信息 .
另外 , 也有一个指向进程虚存区表 (vm_area_struct: virtual memory area) 的指针 , 该链是按照虚拟地址的增长顺
序排列的 . 在 Linux 进程的地址空间被分作许多区 (vma), 每个区 (vma) 都对应虚拟地址空间上一段连续的区域 ,
vma 是可以被共享和保护的独立实体 , 这里的 vma 就是前面提到的内存对象 . 下面是 vm_area_struct 的结构 , 其
中, 前半部分是公共的 , 与类型无关的一些数据成员 , 如 : 指向 mm_struct 的指针 , 地址范围等等 , 后半部分则是
与类型相关的成员 , 其中最重要的是一个指向 vm_operation_struct 向量表的指针 vm_ops, vm_pos 向量表是一组
虚函数 , 定义了与 vma 类型无关的接口 . 每一个特定的子类 , 即每种 vma 类型都必须在向量表中实现这些操作 .
这里包括了 : open, close, unmap, protect, sync, nopage, wppage, swapout 这些操作 .
struct vm_area_struct {
/*公共的 , 与 vma 类型无关的 */
struct mm_struct * vm_mm;
unsigned long vm_start;
unsigned long vm_end;
struct vm_area_struct *vm_next;
评论11
最新资源