博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux内存管理 (9)mmap
阅读量:6441 次
发布时间:2019-06-23

本文共 4360 字,大约阅读时间需要 14 分钟。

专题:

关键词:文件映射、匿名映射、私有映射、共享映射

 

mmap/munmap是常用的一个系统调用,使用场景是:分配内存、读写大文件、连接动态库文件、多进程间共享内存。

更详细解读参考《》。

1. mmap/munmap介绍

mmap/munmap函数声明如下:

#include 
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);
  • addr:如果不为NULL,内核会在此地址创建映射;否则,内核会选择一个合适的虚拟地址。

  • length:表示映射到进程地址空间的大小。

  • prot:内存区域的读/写/执行属性。

  • flags:内存映射的属性,共享、私有、匿名、文件等。

  • fd:表示这是一个文件映射,fd是打开文件的句柄。
  • offset:在文件映射时,表示相对文件头的偏移量;返回的地址是偏移量对应的虚拟地址。

下面是prot对应的参数组合:

#define PROT_READ    0x1        /* page can be read */#define PROT_WRITE    0x2        /* page can be written */#define PROT_EXEC    0x4        /* page can be executed */#define PROT_SEM    0x8        /* page may be used for atomic ops */#define PROT_NONE    0x0        /* page can not be accessed */#define PROT_GROWSDOWN    0x01000000    /* mprotect flag: extend change to start of growsdown vma */#define PROT_GROWSUP    0x02000000    /* mprotect flag: extend change to end of growsup vma */

flags参数组合有:

#define MAP_SHARED    0x01        /* Share changes */---------创建一个共享映射的区域,多个进程可以映射到一个文件,掐进程可以看到映射内容的改变,修改后内容会同步到磁盘中。#define MAP_PRIVATE    0x02        /* Changes are private */--创建一个私有的写时复制的映射,其他进程看不到映射内容的改变,也不会同步到磁盘中。#define MAP_TYPE    0x0f        /* Mask for type of mapping */#define MAP_FIXED    0x10        /* Interpret addr exactly */-使用指定的映射起始地址,如果有start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。#define MAP_ANONYMOUS    0x20        /* don't use a file */---匿名映射,映射区不与任何文件关联。此时fd应设置为-1。#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED# define MAP_UNINITIALIZED 0x4000000    /* For anonymous mmap, memory could be uninitialized */#else# define MAP_UNINITIALIZED 0x0        /* Don't support this flag */#endif#define MAP_GROWSDOWN    0x0100        /* stack-like segment */--------------告诉内核VM系统,映射区可以向下扩展。#define MAP_DENYWRITE    0x0800        /* ETXTBSY */#define MAP_EXECUTABLE    0x1000        /* mark it as an executable */#define MAP_LOCKED    0x2000        /* pages are locked */-------------------锁定映射区页面,从而防止页面被交换出内存。#define MAP_NORESERVE    0x4000        /* don't check for reservations */#define MAP_POPULATE    0x8000        /* populate (prefault) pagetables */---对文件映射来说,会提前预读文件内容到映射区域,只支持私有映射。#define MAP_NONBLOCK    0x10000        /* do not block on IO */--------------和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在与内存中的页面建立页表入口。#define MAP_STACK    0x20000        /* give out an address that is best suited for process/thread stacks */#define MAP_HUGETLB    0x40000        /* create a huge page mapping */

 

2. mmap映射类型

根据mmap是否映射到文件、是共享还是私有映射,将映射类型分成四类,使用场景如下:

场景 私有影射 共享映射
匿名映射

通常用于内存分配

fd=-1,flags=MAP_ANONYMOUS|MAP_PRIVATE

通常用于进程间内存共享,常用于父子进程之间通信。

FD=-1,flags=MAP_ANONYMOUS|MAP_SHARED

文件映射

通常用于加载动态库

flags=MAP_PRIVATE

通常用于内存映射IO、进程间通信、读写文件。

flags=MAP_SHARED

3. mmap流程

 用户空间的mmap,在内核中的起点是mmap_pgoff。

4. mmap使用两个小问题

问题1:两次对相同地址执行mmap是否成功?

#include 
#include
void main(void){ char *pmap1, *pmap2; pmap1 = (char *)mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if(MAP_FAILED == pmap1) printf("pmap1 failed\n"); pmap2 = (char *)mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if(MAP_FAILED == pmap2) printf("pmap1 failed\n");}

 在Ubuntu上执行strace ./mmap结果如下:

...mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000...

 

如果将第二个mmap的MAP_FIXED去掉呢?结果如下:

...mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcb336e1000...

 

 可以看出如果映射区属性包含MAP_FIXED,则会覆盖原来区域;如果没有MAP_FIXED,内核会找到一个区域。

unsigned long mmap_region(struct file *file, unsigned long addr,        unsigned long len, vm_flags_t vm_flags, unsigned long pgoff){...munmap_back:    if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) {        if (do_munmap(mm, addr, len))-----------------------------将冲突区域去映射            return -ENOMEM;        goto munmap_back;    }...}

 

问题2:在一个播放系统中同时打开几十个不同高清视频文件,发现播放有些卡顿,打开文件使用的是mmap,分析原因并解决。

mmap建立文件映射时,只建立了VMA,而没有分配对应的页面和建立映射关系。

播放时会不同发生缺页异常去读取文件内容,导致性能较差。

解决方法:1.对mmap映射后的地址用madvise(addr, len, MADV_SEQUENTIAL)。

                  2.通过"blockdev --setra"来增大内核默认预读窗口,默认是128KB。

5. madvise

 

转载地址:http://jtdwo.baihongyu.com/

你可能感兴趣的文章
Flask即插视图与tornado比较
查看>>
新手教程:建立网站的全套流程与详细解释
查看>>
Objective-C 中nil/Nil/NULL/NSNull
查看>>
细聊分布式ID生成方法
查看>>
js实现全选反选功能
查看>>
string和stringstream用法总结
查看>>
第四周作业
查看>>
使用msf对tomcat测试
查看>>
Linux上的文件查找工具之locate与find
查看>>
0309 复利计算
查看>>
tomcat的基本运行原理解析
查看>>
质量属性3
查看>>
五个案例明白GCD死锁
查看>>
点滴积累【C#】---C#实现上传照片到物理路径,并且将地址保存到数据库,
查看>>
ICS g-sensor,light sensor移植记录
查看>>
入职培训笔记记录--day7(1、指针数组与数组指针 2、函数)
查看>>
golang zip 压缩,解压(含目录文件)
查看>>
二分计算x的n次方
查看>>
0505.Net基础班第八天(飞行棋)
查看>>
安卓高手之路之 WindowManager
查看>>