内存映射的应用:

  • 以页面为单位,将一个普通文件映射到内存中,通常在须要对文件进行频繁读写时使用,这样用内存读写代替I/O读写,以获得较高的性能;
  • 将特殊文件进行匿名内存映射,能够为关联进程提供共享内存空间;
  • 为无关联的进程提供共享内存空间。一般也是将一个普通文件映射到内存中。

相关API

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
void *mmap64(void *addr, size_t length, int prot, int flags,
int fd, off64_t offset);
int munmap(void *addr, size_t length); int msync(void *addr, size_t length, int flags);

mmap函数说明:

  1. 參数 addr 指明文件描写叙述字fd指定的文件在进程地址空间内的映射区的開始地址。必须是页面对齐的地址,通常设为 NULL,让内核去选择開始地址。不论什么情况下,mmap 的返回值为内存映射区的開始地址。

  2. 參数 length 指明文件须要被映射的字节长度。

    off 指明文件的偏移量。通常 off 设为 0 。

    • 假设 len 不是页面的倍数,它将被扩大为页面的倍数。扩充的部分通常被系统置为 0 ,并且对其改动并不影响到文件。

    • off 相同必须是页面的倍数。通过 sysconf(_SC_PAGE_SIZE) 能够获得页面的大小。

  3. 參数 prot 指明映射区的保护权限。通常有下面 4 种。

    一般是 PROT_READ | PROT_WRITE 。

    • PROT_READ 可读
    • PROT_WRITE 可写
    • PROT_EXEC 可运行
    • PROT_NONE 不能被訪问
  4. 參数 flag 指明映射区的属性。取值有下面几种。MAP_PRIVATE 与 MAP_SHARED 必选其一,MAP_FIXED 为可选项。

    • MAP_PRIVATE 指明对映射区数据的改动不会影响到真正的文件。
    • MAP_SHARED 指明对映射区数据的改动,多个共享该映射区的进程都能够看见,并且会反映到实际的文件。
    • MAP_FIXED 要求 mmap 的返回值必须等于 addr 。

      假设不指定 MAP_FIXED 并且 addr 不为 NULL 。则对 addr 的处理取决于详细实现。考虑到可移植性,addr 通常设为 NULL ,不指定 MAP_FIXED。

  5. 当 mmap 成功返回时,fd 就能够关闭,这并不影响创建的映射区。

munmap函数说明:

进程退出的时候,映射区会自己主动删除。

只是当不再须要映射区时。能够调用 munmap 显式删除。

当映射区删除后。兴许对映射区的引用会生成 SIGSEGV 信号。

msync函数说明:

文件一旦被映射后,调用mmap()的进程对返回地址的訪问是对某一内存区域的訪问,临时脱离了磁盘上文件的影响。全部对mmap()返回地址空间的操作仅仅在内存中有意义,仅仅有在调用了munmap()后或者msync()时,才把内存中的对应内容写回磁盘文件。

代码实例:

两个进程通过映射普通文件实现共享内存通信

map_normalfile1.c及map_normalfile2.c。编译两个程序。可运行文件分别为map_normalfile1及map_normalfile2。两个程序通过命令行參数指定同一个文件来实现共享内存方式的进程间通信。map_normalfile2试图打开命令行參数指定的一个普通文件。把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。

map_normalfile1把命令行參数指定的文件映射到进程地址空间,然后对映射后的地址空间运行读操作。

这样,两个进程通过命令行參数指定同一个文件来实现共享内存方式的进程间通信。

/*-------------map_normalfile1.c-----------*/
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h> typedef struct{
char name[4];
int age;
}people; int main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp[2] = {'\0'}; fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1); p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0 );
close( fd );
temp[0] = 'a';
for(i=0; i<15; i++)
{
temp[0] += 1;
memcpy( ( *(p_map+i) ).name, &temp[0],2 );
( *(p_map+i) ).age = 20+i;
}
printf("initialize over\n");
sleep(10);
munmap( p_map, sizeof(people)*10 );
printf( "umap ok \n" ); return 0;
}
/*-------------map_normalfile2.c-----------*/
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h> typedef struct{
char name[4];
int age;
}people; int main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp[2] = {'\0'}; fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,sizeof(people)*5-1,SEEK_SET);
write(fd,"",1); p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0 );
close( fd );
temp[0] = 'a';
for(i=0; i<15; i++)
{
temp[0] += 1;
memcpy( ( *(p_map+i) ).name, &temp[0],2 );
( *(p_map+i) ).age = 20+i;
}
printf("initialize over\n");
sleep(10);
munmap( p_map, sizeof(people)*10 );
printf( "umap ok \n" ); return 0;
}

map_normalfile1首先打开或创建一个文件,并把文件的长度设置为5个people结构大小.mmap映射10个people结构大小的内存,利用返回的地址開始设置15个people结构。然后睡眠10S,等待其它进程映射同一个文件,然后解除映射。

通过实验,在map_normalfile1输出initialize over 之后,输出umap ok之前,运行map_normalfile2 file,能够输出设置好的15个people结构

在map_normalfile1 输出umap ok后。运行map_normalfile2则输出结构,前5个people是已设置的,后10结构为0。

1) 终于被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小.

2) 能够用于进程通信的有效地址空间大小大体上受限于被映射文件的大小。但不全然受限于文件大小.打开文件的大小为5个people结构,映射长度为10个people结构长度,共享内存通信用15个people结构大小。

在linux中。内存的保护是以页为基本单位的。即使被映射文件仅仅有一个字节大小。内核也会为映射分配一个页面大小的内存。

当被映射文件小于一个页面大小时。进程能够对从mmap()返回地址開始的一个页面大小进行訪问,而不会出错;可是,假设对一个页面以外的地址空间进行訪问,则导致发生错误,后面将进一步描写叙述。因此。可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。

3) 文件一旦被映射后,调用mmap()的进程对返回地址的訪问是对某一内存区域的訪问。临时脱离了磁盘上文件的影响。全部对mmap()返回地址空间的操作仅仅在内存中有意义,仅仅有在调用了munmap()后或者msync()时,才把内存中的对应内容写回磁盘文件,所写内容仍然不能超过文件的大小

技巧:

生成固定大小的文件的两种方式:

/*第一种方法*/
fd = open(PATHNAME, O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
lseek(fd, filesize-1, SEEK_SET);
write(fd, "", 1); /*另外一种方法*/
ftruncate(fd, filesize);

父子进程通过匿名映射实现共享内存

  1. 匿名内存映射 与 使用 /dev/zero 类型。都不须要真实的文件。要使用匿名映射之须要向 mmap 传入 MAP_ANON 标志,并且 fd 參数 置为 -1 。
  2. 所谓匿名。指的是映射区并没有通过 fd 与 文件路径名相关联。

    匿名内存映射用在有血缘关系的进程间。

#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv)
{
int i;
people *p_map;
char temp;
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS,-1,0);
if(fork() == 0)
{
sleep(2);
for(i = 0;i<5;i++)
printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);
(*p_map).age = 100;
munmap(p_map,sizeof(people)*10); //实际上,进程终止时。会自己主动解除映射。
exit();
}
temp = 'a';
for(i = 0;i<5;i++)
{
temp += 1;
memcpy((*(p_map+i)).name, &temp,2);
(*(p_map+i)).age=20+i;
}
sleep(5);
printf( "parent read: the first people,s age is %d\n",(*p_map).age );
printf("umap\n");
munmap( p_map,sizeof(people)*10 );
printf( "umap ok\n" );
}

參考:

共享内存:mmap函数实现的更多相关文章

  1. c#读写共享内存操作函数封装

    原文 c#读写共享内存操作函数封装 c#共享内存操作相对c++共享内存操作来说原理是一样,但是c#会显得有点复杂. 现把昨天封装的读写共享内存封装的函数记录下来,一方面希望给需要这块的有点帮助,另一方 ...

  2. linux下共享内存mmap和DMA(直接访问内存)的使用 【转】

    转自:http://blog.chinaunix.net/uid-7374279-id-4413316.html 介绍Linux内存管理和内存映射的奥秘.同时讲述设备驱动程序是如何使用“直接内存访问” ...

  3. 共享内存mmap学习 及与 shmxxx操作的区别

    上一篇学习了共享内存: http://www.cnblogs.com/charlesblc/p/6142139.html 根据这个 http://blog.chinaunix.net/uid-2633 ...

  4. 【APUE】进程间通信之共享存储(mmap函数)

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只 ...

  5. 【Linux 应用编程】进程管理 - 进程间通信IPC之共享内存 mmap

    IPC(InterProcess Communication,进程间通信)是进程中的重要概念.Linux 进程之间常用的通信方式有: 文件:简单,低效,需要代码控制同步 管道:使用简单,默认阻塞 匿名 ...

  6. linux 进程间通信 共享内存 mmap

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反 ...

  7. system v和posix的共享内存对比 & 共享内存位置

    参考 http://www.startos.com/linux/tips/2011012822078.html 1)Linux和所有的UNIX操作系统都允许通过共享内存在应用程序之间共享存储空间. 2 ...

  8. IPC_共享内存

    在IPC(InterProcess Communication)的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key).通过“ ...

  9. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6664554 在上一文章Android系统匿名共 ...

随机推荐

  1. Apache URL重写的配置 及其 apache500错误

    1:如果apache报500错误时 ----->原因:可能是你的ReWrite模块没有打开(有时在apache重装时会忘记打开该模块) 将apache--->httpd.conf文件中Lo ...

  2. [转]QT QDateTime类、QTimer类

    QDateTime类,头文件#include <QDateTime> 可以使用QDateTime类来获得系统时间.通过QDateTime::currentDateTime()来获取本地系统 ...

  3. MVC中导航菜单,选中项的高亮问题。

      这个菜单是放在母板页的.比如当前选中的是异常业务监控.如果页面刷新了.就会变成第一张图..选择其他的选项也会,因为页面会刷新嘛.. 怎么处理这个问题了? 答案是记录当前页面的url. 有两种解决思 ...

  4. 文字编码和Unicode

    文字编码和Unicode 说明文字: https://blog.csdn.net/fengzhishang2019/article/details/7859064 Java 程序: https://w ...

  5. 基于VM10+Win7安装Mac OSX10.11 El Capitan

    前言 此文写给那些像我一样的屌丝程序员(呵呵,我现在从事的是最底层的工作了,但是不想放弃我的梦想) 说明 基于VM10+Win7安装Mac OSX10.11 El Capitan 工具 VMware- ...

  6. (纪录片)统计的乐趣 The Joy of Stats (2010)

    简介: 导演: 丹·希尔曼主演: Hans Rosling类型: 纪录片官方网站: www.bbc.co.uk/programmes/b00wgq0l制片国家/地区: 英国语言: 英语上映日期: 20 ...

  7. android studio中的常用快捷键

    1.Ctrl+Alt+Space 这个类似Eclipse中的Alt+/,实现智能提示功能的 2.Ctrl+Y 删除当前行,Eclipse中是Ctrl+D,伤不起,每次都习惯性的按Ctrl+D,不删,反 ...

  8. WPF编程:textbox控件文本框数据显示最后一行

    WPF编程:textbox控件文本框数据显示最后一行 TextBox控件在接收大量数据的时候,滚动条一般在最上方,如何使滚动条随着数据的接收而向下滚动呢?比如有一个TextBox'控件txbRecvD ...

  9. 生日日期联动选择birthday.js

    实例下载

  10. 微软BI 之SSRS 系列 - 使用 LookupSet 和 Adjacent Group 等高级技巧在报表中跨 Dataset 分组查询

    SSRS 报表中有一些高级的技巧,平常很少用到,下面我通过这个案例来展现一下如何在实际开发中使用它们,并且如何解决一些实际的需求. 这张报表分别统计了不同的 Product 产品在不同的月份的 Ord ...