brk() 和 mmap() 内存映射
参考博文:http://www.cnblogs.com/huxiao-tee/p/4660352.html
- x86平台下linux进程虚拟地址空间分布(2.6.7以前版本)
mmap区域与栈区域相对增长,只有1GB连续的虚拟地址空间可用。
- x86平台下linux进程虚拟地址空间分布(2.6.7以后版本)
Random stack offset:由于之前栈的地址是固定的,容易被人利用栈溢出进行攻击,这里栈每次有一个偏移量。
RLIMIT_STACK:向栈中压入数据容量超过栈的容量时,会触发page fault,异常会检测到最近的虚拟地址空间,发现产生异常的地址与栈相邻,会扩大栈的大小(一般是8M)。如果栈被加长,栈针回退时不会再收缩,如果stack overflow则会导致segment fault。
Memory Mapping Segment:内存映射的位置,一种高效I/O,后面会细说。
- 对heap的操作函数 brk() 和 sbrk()
int brk(void *addr);
void sbrk(intptr_t increment);
内核数据结构mm_struct中 start_brk是进程动态分配的起始地址(heap的起始地址),brk 是堆当前最后的地址。
首先program break就是当前brk的位置,所以他是数据段初始化结束后heap的第一个位置,而不是heap的尾部。
sbrk()是库函数,brk()是系统调用,相对于库函数来说一般系统调用会提供相对简单的工作。都是改变brk的值来扩展收缩堆(increment 为负数时收缩)。
- mmap 映射区函数
1.基础概念
mmap 是一种内存映射方法,将一个文件或其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址一一对应的关系。内核空间对这块区域的改变也直接反应到用户空间,实现不同进程的文件共享。
linux内核使用vm_area_struct结构表示一个独立的虚拟内存区域,一个进程使用多个vm_area_struct来分别表示不同类型的虚拟内存区域.
当vm_area_struct数目较少时,按照升序以单恋表的形式组织结构,在数目较多时使用AVL树来实现。
v
mmap函数是创建一个新的vm_area_struct结构,并将其与物理地址相连。
2. mmap内存映射原理
分为三个阶段
- 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
1>进程在用户空间调用mmap。
原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
2>在当前进程的虚拟地址空间中,寻找一段空间满足要求的连续的虚拟地址。
3>为此虚拟区分配一个vm_area_struct 结构,接着对这个结构的各个域进行初始化
4>将新建的虚拟结构(vm_area_struct)插入到进程的虚拟地址区的链表或数种。
- 调用内核空间的系统调用函数mmap(不同于用户空间)实现文件物理地址和进程虚拟地址的一一映射关系
5>为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,加入到struct file中
6>linux中的file_operation结构中定义了不同事件对应的设备驱动函数,其中有 int mmap(struct file *filp, struct vm_area_struct *vma),其实这个函数就是将用户空间与设备内存相连,也就是对虚拟地址的访问转化为对设备的访问
7>通过inode模块找到对应的文件,也就是磁盘的物理地址
8>建立页表,实现文件地址和虚拟地址区域的映射关系。这里只建立了映射关系,主存中没有对应物理地址的数据。
- 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到主存的拷贝
9>进程的读写,通过查询页表发现这一段地址不再物理页面上,引发缺页异常
10>进行缺页异常判断,申请调页
11>先判断swap cache中没有没需要访问的内存页,如果没有调用nopage把所缺德页从磁盘装入主存
12>之后可以进行读写,会有一段时间延迟,调用msync()立即更新。
3. mmap优点总结
1>对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,使用内存读取代了I/O操作,提高了文件读取效率。
2>实现了用户空间和内核空间的高效交互方式
3>提供进程间共享内存及相互通信方式
4>实现高效的大规模数据传输
还有内核的高端内存映射:https://www.cnblogs.com/wuchanming/p/4360277.html