专题:Linux内存管理专题

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

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

更详细解读参考《Linux内存管理 (9)mmap(补充)》。

1. mmap/munmap介绍

mmap/munmap函数声明如下:

#include <sys/mman.h>
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 <stdio.h>
#include <sys/mman.h> void main(void)
{
char *pmap1, *pmap2; pmap1 = (char *)mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, );
if(MAP_FAILED == pmap1)
printf("pmap1 failed\n"); pmap2 = (char *)mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, );
if(MAP_FAILED == pmap2)
printf("pmap1 failed\n");
}

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

...
mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0x20000000
mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0x20000000
...

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

...
mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0x20000000
mmap(0x20000000, , PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 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

Linux内存管理 (9)mmap的更多相关文章

  1. Linux内存管理 (9)mmap(补充)

    之前写过一篇简单的介绍mmap()/munmap()的文章<Linux内存管理 (9)mmap>,比较单薄,这里详细的梳理一下. 从常用的使用者角度介绍两个函数的使用:然后重点是分析内核的 ...

  2. Linux内存管理之mmap详解

    转发之:http://blog.chinaunix.net/uid-26669729-id-3077015.html Linux内存管理之mmap详解 一. mmap系统调用 1. mmap系统调用  ...

  3. Linux 内存管理之mmap详解

    找了好多,最后发现下面这篇时讲的比较通俗易懂的. Linux内存管理之mmap详解-heavent2010-ChinaUnix博客 http://blog.chinaunix.net/uid-2666 ...

  4. 转:Linux内存管理之mmap详解

    一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零.munmap执行相 ...

  5. [转载] Linux内存管理之mmap详解

    转载自http://blog.chinaunix.net/uid-26669729-id-3077015.html 一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进 ...

  6. Linux内存管理之mmap详解 【转】

    转自:http://blog.chinaunix.net/uid-26669729-id-3077015.html 一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进 ...

  7. Linux内存管理之mmap详解 (可用于android底层内存调试)

    注:将android底层malloc换为mmap来获取内存,可将获取到的内存添加tag,从而再利用meminfo进行分析,可单独查看该tag的内存,从而进行分析. 一. mmap系统调用 1. mma ...

  8. linux内存管理

    一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...

  9. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

随机推荐

  1. leetcode — palindrome-partitioning

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Source : https://o ...

  2. .NET Core GC 的设计

    此文章转载自:http://www.cnblogs.com/zkweb/p/6288457.html 作者: Maoni Stephens ( @maoni0) - 2015 提示: 推荐看 The ...

  3. [三] java虚拟机 JVM字节码 指令集 bytecode 操作码 指令分类用法 助记符

    说明,本文的目的在于从宏观逻辑上介绍清楚绝大多数的字节码指令的含义以及分类 只要认真阅读本文必然能够对字节码指令集有所了解 如果需要了解清楚每一个指令的具体详尽用法,请参阅虚拟机规范 指令简介 计算机 ...

  4. python基础3--函数

    1.函数定义 你可以定义一个由自己想要功能的函数,以下是简单的规则: 函数代码块以def关键词开头,后接函数标识符名称和圆括号(). 任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数 ...

  5. shell编程练习(二): 笔试11-20

    笔试练习(二): 11.写一个shell脚本来得到当前的日期,时间,用户名和当前工作目录. [root@VM_0_5_centos test]# vi 11.sh [root@VM_0_5_cento ...

  6. 第29章 保护API - Identity Server 4 中文文档(v1.0.0)

    IdentityServer 默认以JWT(JSON Web令牌)格式发出访问令牌. 今天的每个相关平台都支持验证JWT令牌,这里可以找到一个很好的JWT库列表.热门库例如: ASP.NET Core ...

  7. GITHup的使用

    一个源码管理工具,由于不擅长敲GIt命令,还不太喜欢用英文版本的软件,所以想办法用中文版的图形工具步骤如下: 下载了GIT64位,安装,下载了TortoiseGit和TortoiseGit中文语言包, ...

  8. PhpStudy升级MySQL5.7

    PhpStudy2017集成环境中的mysql数据库的版本默认是mysql5.5,下面是PhpStudy升级数据库到mysql5.7的方法: 1:备份当前数据库数据,可以导出数据库文件,作为备份,我这 ...

  9. Win10系统给文件夹添加备注

    在Win10系统中,相信大多用户都没有看到过文件或者是文件夹上有备注信息.下面给大家分享下在Win10系统中给文件夹或文件添加备注的方法.在添加备注之前,首先我们要在需要显示备注的文件夹中显示&quo ...

  10. Elasticsearch系列(5):深入搜索

    结构化搜索 结构化搜索是指搜索那些具有内置结构数据的过程,比如日期,时间和数字都是结构化的,它们有精确的格式,我们可以对这些格式进行逻辑操作,比较常见的操作包括比较数字或时间的范围,或判定两个值的大小 ...