共享内存之——mmap内存映射

共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过mmap()映射普通文件 (特殊情况下还可以采用匿名映射)机制实现,也可以通过systemV共享内存机制实现。应用接口和原理很简单,内部机制复杂。为了实现更安全通信,往往还与信号灯等同步机制共同使用。 这一篇详解mmap内存文件映射原理及其案例,system V共享内存 以及他们的区别将在后面的随笔中讨论。



非原创,内容源于互联网

mmap内存文件映射

一、传统文件访问

unix访问文件的传统方法使用open打开他们,如果有多个进程访问一个文件,则每一个进程在再记得地址空间都包含有该文件的副本,这不必要地浪费了存储空间。下面说明了两个进程同时读一个文件的同一页的情形,系统要将该页从磁盘读到高速缓冲区中,每个进程再执行一个内存期内的复制操作将数据从高速缓冲区读到自己的地址空间。

二、共享内存映射

现在考虑林一种处理方法:进程A和进程B都将该页映射到自己的地址空间,当进程A第一次访问该页中的数据时,它生成一个缺页终端,内核此时读入这一页到内存并更新页表使之指向它,以后,当进程B访问同一页面而出现缺页中断时,该页已经在内存,内核只需要将进程B的页表登记项指向次页即可。

三、mmap及其相关系统调用

mmap()系统调用使得进城之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write

等操作。

mmap()系统调用形式如下:

#include<sys/mman.h>

void mmap(void *addr, size_t len, int prot,int flags, int fildes, off_t off)
int msync(void *addr, size_t len, int flags);
int munmap(void *addr, size_t len);

mmap的作用是映射文件描述符和指定文件的(off_t off)区域至调用进程的(addr,addr *len)的内存区域,如下图所示:

参数:

  fd:为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间进行通信)。

  len:是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。

  prot:指定空想内存的访问权限。可取如下几个值的或:PROT_READ(可读)、PROT_WRITE(可写)、PROT_EXEC(可执行)、PROT_NONE(不可访问)。

  flag:由以下几个常值指定:MAP_SHARED、MAP_PRIVATE、MAP_FIXED,其中,MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。

  offset:一般设为0,表示从文件头开始映射。

  addr:指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。

函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。

四、mmap基础用例

1 //测试文件 data.txt 后面的程序也要用到
2 aaaaaaaaa
3 bbbbbbbbb
4 cccccccccccc
5 ddddddddd

1、通过共享内存映射的方式修改文件

 1 #include <sys/mman.h>
 2 #include <sys/stat.h>
 3 #include<fcntl.h>
 4 #include<stdio.h>
 5 #include<stdlib.h>
 6 #include<unistd.h>
 7 #include<error.h>
 8
 9 int main(int argc, char * argv[])
10 {
11      int fd, nread;
12      struct stat sb;
13      char *mapped;
14
15 //打开文件
16       if((fd = open(argv[1], O_RDWR)) < 0){
17            perror("open") ;
18       }
19
20 //获取文件的属性
21       if((fstat(fd, &sb)) == -1 ){
22            perror("fstat") ;
23       }
24
25
26 //将文件映射至进程的地址空间
27       if((mapped = mmap(NULL, sb.st_size, PROT_READ|28                PROT_WRITE, MAP_SHARED, fd, o)) ==(void*) -1){
29            perror("mmap") ;
30       }
31
32 //修改一个字符,同步到磁盘文件
33       mapped[20] = ‘9‘;
34       if((msync((void *)mapped, sb.st_size, MS_SYNC)) == -1){
35            perror("msync") ;
36
37 //释放存储映射区
38       if((munmap((void *)mapped,sb.st_size)) == -1){
39            perror("munmap");
40       }
41
42       return 0;
43 }
44   

2 私有映射无法修改文件

1 //将文件私有映射到进程的地址空间
2 if((mapped = (char *)mmap(NULL,sb.st_size,PROT_READ|
3                     PROT_WRITE, MAP_PRIVATE, fd, 0))==(void *)-1){
4         perror("mmap");

五、使用共享内存映射实现两个进程之间的通信

两个程序映射到同一个文件到自己的地址空间,进程A先运行,每个两秒读取映射区域,看是否发生变化,进程B后运行,它修改映射区域,然后退出,此时进程A能够观察到存储映射区的变化

进程A的代码:

 1 #include <sys/mman.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <unistd.h>
 7 #include <error.h>
 8
 9 int main(int argc, char **argv)
10 {
11         int fd, nread;
12         struct stat sb;
13         char *mapped;
14
15
16 /* 打开文件 */
17         if ((fd = open(argv[1], O_RDWR)) < 0) {
18             perror("open");
19         }
20
21 /* 获取文件的属性 */
22         if ((fstat(fd, &sb)) == -1) {
23             perror("fstat");
24         }
25
26 /* 将文件映射至进程的地址空间 */
27         if ((mapped = (char *)mmap(NULL, sb.st_size, PROT_READ            | PROT_WRITE, MAP_SHARED, fd, 0)) == (void *)-1) {
28             perror("mmap");
29         }
30
31 /* 文件已在内存, 关闭文件也可以操纵内存 */
32         close(fd);
33
34 /* 每隔两秒查看存储映射区是否被修改 */
35         while (1) {
36             printf("%s\n", mapped);
37             sleep(2);
38         }
39
40         return 0;
41 }   

进程B的代码

 1   #include <sys/mman.h>
 2   #include <sys/stat.h>
 3   #include <fcntl.h>
 4   #include <stdio.h>
 5   #include <stdlib.h>
 6   #include <unistd.h>
 7   #include <error.h>
 8
 9   int main(int argc, char **argv)
10  {
11          int fd;
12          struct stat sb;
13          char *mapped;
14
15  /* 打开文件 */
16          if ((fd = open(argv[1], O_RDWR)) < 0) {
17              perror("open");
18          }
19
20  /* 获取文件的属性 */
21          if ((fstat(fd, &sb)) == -1) {
22              perror("fstat");
23          }
24  /* 私有文件映射将无法修改文件 */
25          if ((mapped = (char *)mmap(NULL, sb.st_size, PROT_READ
26                     |PROT_WRITE,MAP_PRIVATE, fd, 0)) == (void*)-1) {
27              perror("mmap");
28          }
29
30  /* 映射完后, 关闭文件也可以操纵内存 */
31          close(fd);
32
33  /* 修改一个字符 */
34          mapped[20] = ‘9‘;
35
36          return 0;
37  }  

六、通过匿名映射实现父子进程通信

 1 #include <sys/mman.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5
 6  #define BUF_SIZE 100
 7
 8 int main(int argc, char** argv)
 9 {
10         char    *p_map;
11
12 /* 匿名映射,创建一块内存供父子进程通信 */
13         p_map = (char *)mmap(NULL, BUF_SIZE, PROT_READ |      PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
14
15         if(fork() == 0) {
16             sleep(1);
17             printf("child got a message: %s\n", p_map);
18             sprintf(p_map, "%s", "hi, dad, this is son");
19             munmap(p_map, BUF_SIZE); //实际上,进程终止时,会自动解除映射。
20             exit(0);
21         }
22
23         sprintf(p_map, "%s", "hi, this is father");
24         sleep(2);
25         printf("parent got a message: %s\n", p_map);
26
27         return 0;
28 }  

七、对mmap()返回地址的访问

linux采用的是页式管理机制。对于用mmap()映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由mmap()的len参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从mmap()返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:

总结一下就是,文件大小,mmap()的参数len都不能决定进程能访问的大小,而是容纳文件被映射部分的最小页面数决定进程能访问的大小,下面看一个实例:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>  

 int main(int argc, char** argv)
 {
        int fd,i;
        int pagesize,offset;
        char *p_map;
        struct stat sb;  

 /* 取得page size */
        pagesize = sysconf(_SC_PAGESIZE);
        printf("pagesize is %d\n",pagesize);  

/* 打开文件 */
        fd = open(argv[1], O_RDWR, 00777);
        fstat(fd, &sb);
        printf("file size is %zd\n", (size_t)sb.st_size);  

        offset = 0;
        p_map = (char *)mmap(NULL, pagesize * 2,  PROT_READ|PROT_WRITE,   MAP_SHARED, fd, offset);
        close(fd);  

        p_map[sb.st_size] = ‘9‘;  /* 导致总线错误 */
        p_map[pagesize] = ‘9‘;    /* 导致段错误 */  

        munmap(p_map, pagesize * 2);  

       return 0;
}  
时间: 2024-08-04 12:53:58

共享内存之——mmap内存映射的相关文章

Linux之共享内存shm和内存映射mmap

一.共享内存shm 1 概念:多个进程的地址空间都映射到同一块物理内存,这样多个进程都能看到这块物理内存,实现进程间通信,而且不需要数据的拷贝,所以速度最快. 二.内存映射mmap 1 前言:先介绍一下普通的读写文件的原理,进程调用read/write系统调用后会陷入内核,内核开始读写文件,假设内核是在读文件,内核先把文件读取到内核缓冲区,然后把内核缓冲区的数据拷贝到用户缓冲区,实际上整个过程拷贝了两次数据,即先从文件到内核缓冲区,再从内核缓冲区到用户缓冲区: 2 概念:把某个文件映射到进程的地

Linux的mmap内存映射机制解析

在讲述文件映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM).实际上,文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施,好似用户将文件映射到自己地址空间的某个部分,使用简单的内存访问指令读写文件:另一方面,它也可以用于内核的基本组织模式,在这种模式种,内核将整个地址空间视为诸如文件之类的一组不同对象的映射.中的传统文件访问方式是,首先用open系统调用打开文件,然后使用read, write以及lseek等调用进行顺序或者随即的I/O.这种方式是非常低效的,每一次I/O操作都

Python之mmap内存映射模块(大文本处理)说明

背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力.关于sed的说明可以看了解sed的工作原理,本文将介绍通过python的mmap模块来实现对大文件的处理,来对比看他们的差异. 说明: mmap是一种虚拟内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.关于系统中mmap的理论说明可以看百度百科和维基百科说明以及mmap函数介

linux mmap 内存映射【转】

转自:http://blog.csdn.net/xyyangkun/article/details/7830313 [-] mmap vs readwritelseek mmap vs malloc mmap共享内存进程通信 总结 http://www.perfgeeks.com/?p=723 mmap() vs read()/write()/lseek() 通过strace统计系统调用的时候,经常可以看到mmap()与mmap2().系统调用mmap()可以将某文件映射至内存(进程空间),如此

【转】Python之mmap内存映射模块(大文本处理)说明

[转]Python之mmap内存映射模块(大文本处理)说明 背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力.关于sed的说明可以看了解sed的工作原理,本文将介绍通过python的mmap模块来实现对大文件的处理,来对比看他们的差异. 说明: mmap是一种虚拟内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.关于系统中mm

[转载] 写mmap内存和文件产生几百ms延迟原因

原文: http://weibo.com/p/1001603830912709174661 写mmap内存和文件产生几百ms延迟原因 2015年4月12日 21:10 阅读 4274 最近看到一个bug介绍,作者花了4个月追踪定位,发现jvm统计会造成垃圾回收过程停顿好几百ms.jvm统计信息会写到一片内存区域中,该区域mmap到/tmp下的文件. 这个Bug的详细情况见下面的链接: The Four Month Bug: JVM statistics cause garbage collect

Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)

1.Java直接内存与堆内存-MarchOn 2.Java内存映射文件-MarchOn 3.Java Unsafe的使用-MarchOn 简单总结: 1.内存映射文件 读文件时候一般要两次复制:从磁盘复制到内核空间再复制到用户空间,内存映射文件避免了第二次复制,且内存分配在内核空间,应用程序访问的就是操作系统的内核内存空间,因此极大提高了读取效率.写文件同理. 2.堆内存分配与直接内存分配: Java申请空间时通常是从JVM堆内存分配的,即 ByteBuffer.allocate(int cap

SAP内存、ABAP内存、共享内存的 区别

区别: (1)SAP内存使用 SET/GET parameters 方法: SET  PARAMETER  ID  'MAT' field P_MATNR. GET  PARAMETER  ID  'MAT' field P_MATNR. EXP: IF GW_TAB-EBELN  IS NOT  INITIAL. SET  PARAMETER  ID  'BES'  FIELD  GW_TAB-EBELN. CALL TRANSACTION  'MW23N'  AND  SKIP  FIRST

DELL笔记本电脑设置BIOS内存的缓存和映射

问: DELL 640m 求怎么把BIOS映射到内存或显卡? 我在优化大师上看见.BIOS可以被映射,我看到网上介绍,通常都是把BIOS映射到内存或显卡上. 答: BIOS映射的作用是将系统的BIOS映射到系统内存中,这样当系统需要读取BIOS信息时,就可以直接从内存中读取,而不需要访问主板的BIOS芯片.由于内存的读取速度比BIOS芯片的读取速度快得多,因此,使用BIOS映射功能可以在一定程度上提高电脑的性能.     如果你的BIOS是Award的,可在开机时按Del键进入BIOS,打开“A