共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制。共享内存可以通过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基础用例

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

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

 #include <sys/mman.h>
#include <sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<error.h> int main(int argc, char * argv[])
{
int fd, nread;
struct stat sb;
char *mapped; //打开文件
if((fd = open(argv[], O_RDWR)) < ){
perror("open") ;
} //获取文件的属性
if((fstat(fd, &sb)) == - ){
perror("fstat") ;
} //将文件映射至进程的地址空间
if((mapped = mmap(NULL, sb.st_size, PROT_READ|\
PROT_WRITE, MAP_SHARED, fd, o)) ==(void*) -){
perror("mmap") ;
} //修改一个字符,同步到磁盘文件
mapped[] = '';
if((msync((void *)mapped, sb.st_size, MS_SYNC)) == -){
perror("msync") ; //释放存储映射区
if((munmap((void *)mapped,sb.st_size)) == -){
perror("munmap");
} return ;
}

2 私有映射无法修改文件

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

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

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

进程A的代码:

 #include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h> int main(int argc, char **argv)
{
int fd, nread;
struct stat sb;
char *mapped; /* 打开文件 */
if ((fd = open(argv[], O_RDWR)) < ) {
perror("open");
} /* 获取文件的属性 */
if ((fstat(fd, &sb)) == -) {
perror("fstat");
} /* 将文件映射至进程的地址空间 */
if ((mapped = (char *)mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, )) == (void *)-) {
perror("mmap");
} /* 文件已在内存, 关闭文件也可以操纵内存 */
close(fd); /* 每隔两秒查看存储映射区是否被修改 */
while () {
printf("%s\n", mapped);
sleep();
} return ;
}

进程B的代码

   #include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h> int main(int argc, char **argv)
{
int fd;
struct stat sb;
char *mapped; /* 打开文件 */
if ((fd = open(argv[], O_RDWR)) < ) {
perror("open");
} /* 获取文件的属性 */
if ((fstat(fd, &sb)) == -) {
perror("fstat");
}
/* 私有文件映射将无法修改文件 */
if ((mapped = (char *)mmap(NULL, sb.st_size, PROT_READ
|PROT_WRITE,MAP_PRIVATE, fd, )) == (void*)-) {
perror("mmap");
} /* 映射完后, 关闭文件也可以操纵内存 */
close(fd); /* 修改一个字符 */
mapped[] = ''; return ;
}

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

 #include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #define BUF_SIZE 100 int main(int argc, char** argv)
{
char *p_map; /* 匿名映射,创建一块内存供父子进程通信 */
p_map = (char *)mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -, ); if(fork() == ) {
sleep();
printf("child got a message: %s\n", p_map);
sprintf(p_map, "%s", "hi, dad, this is son");
munmap(p_map, BUF_SIZE); //实际上,进程终止时,会自动解除映射。
exit();
} sprintf(p_map, "%s", "hi, this is father");
sleep();
printf("parent got a message: %s\n", p_map); return ;
}

七、对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[], O_RDWR, );
fstat(fd, &sb);
printf("file size is %zd\n", (size_t)sb.st_size); offset = ;
p_map = (char *)mmap(NULL, pagesize * , PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
close(fd); p_map[sb.st_size] = ''; /* 导致总线错误 */
p_map[pagesize] = ''; /* 导致段错误 */ munmap(p_map, pagesize * ); return ;
}

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

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

    背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力.关于sed的说明可以看了解sed的工作原理,本文将介绍通过 ...

  2. linux mmap 内存映射【转】

    转自:http://blog.csdn.net/xyyangkun/article/details/7830313 [-] mmap vs readwritelseek mmap vs malloc ...

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

    [转]Python之mmap内存映射模块(大文本处理)说明 背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力 ...

  4. [转载]Linux驱动mmap内存映射

    原文地址:https://www.cnblogs.com/wanghuaijun/p/7624564.html mmap在linux哪里? 什么是mmap? 上图说了,mmap是操作这些设备的一种方法 ...

  5. Linux驱动mmap内存映射

    mmap在linux哪里? 什么是mmap? 上图说了,mmap是操作这些设备的一种方法,所谓操作设备,比如IO端口(点亮一个LED).LCD控制器.磁盘控制器,实际上就是往设备的物理地址读写数据. ...

  6. mmap内存映射

    http://blog.csdn.net/kongdefei5000/article/details/70183119 内存映射是个很有用,也很有意思的思想.我们都知道操作系统分为用户态和内核态,用户 ...

  7. Android mmap 文件映射到内存介绍

    本文链接: Android mmap 文件映射到内存介绍 Android开发中,我们可能需要记录一些文件.例如记录log文件.如果使用流来写文件,频繁操作文件io可能会引起性能问题. 为了降低写文件的 ...

  8. linux mmap 内存映射

    mmap() vs read()/write()/lseek() 通过strace统计系统调用的时候,经常可以看到mmap()与mmap2().系统调用mmap()可以将某文件映射至内存(进程空间), ...

  9. sendfile“零拷贝”和mmap内存映射

    在学习sendfille之前,我们先来了解一下浏览器访问页面时,后台服务器的大致工作流程. 下图是从用户访问某个页面到页面的显示这几秒钟的时间当中,在后台的整个工作过程. 如上图,黑色箭头所示的过程, ...

随机推荐

  1. ios 设计模式总结

    设计模式:备注:消息传递模型(Message Passing)是Objective-C语言的核心机制.在Objective-C中,没有方法调用这种说法,只有消息传递.在C++或Java中调用某个类的方 ...

  2. 深入理解ES6箭头函数的this以及各类this面试题总结

    ES6中新增了箭头函数这种语法,箭头函数以其简洁性和方便获取this的特性,俘获了大批粉丝儿 它也可能是面试中的宠儿, 我们关键要搞清楚 箭头函数和普通函数中的this 一针见血式总结: 普通函数中的 ...

  3. ubuntu 16.04 +anaconda3.6 +Nvidia DRIVER 390.77 +CUDA9.0 +cudnn7.0.4+tensorflow1.5.0+neural-style

    这是我第一个人工智能实验.虽然原理不是很懂,但是觉得深度学习真的很有趣.教程如下. Table of Contents 配置 时间轴 前期准备工作 anaconda3 安装 bug 1:conda:未 ...

  4. 转载:将画布(canvas)图像保存成本地图片的方法

    之前我曾介绍过如何将HTML5画布(canvas)内容转变成图片形式,方法十分简单.但后来我发现只将canvas内容转变成图片输出还不够,如何能将转变后的图片保存到本地呢? 其实,这个方法也是非常简单 ...

  5. 科学计算库Numpy——numpy.ndarray

    创建ndarray 元素类型 对于ndarray结构来说,里面所有的元素必须是同一类型的,如果不是的话,会自动的向下进行转换. 元素类型所占字节数 数组维数 元素个数 数组的维度 数组中填充固定值 索 ...

  6. 线段树:CDOJ1597-An easy problem C(区间更新的线段树)

    An easy problem C Time Limit: 4000/2000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Pr ...

  7. hdu 3836 tarjain 求强连通分量个数

    // 给你一个有向图,问你最少加几条边能使得该图强连通 #include <iostream> #include <cstdio> #include <cstring&g ...

  8. hiho 1050 树的直径

    #1050 : 树中的最长路 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中, ...

  9. hdu2604 递推转换矩阵快速幂

    刚开始还以为用位运算与或几下几个循环就搞定了,算着算着发现不行........ 还是一种固定的切题角度,我假设有长度为n,总的排列数位f(n),怎么算他呢?从后往前考虑,因为大多数情况,都是用前面的结 ...

  10. OpenSSL SNI

    网站ssl检测工具 https://www.ssllabs.com/ssltest/analyze.html?d=gggl.houseoflux.com.cn https://myssl.com/  ...