Linux IPC实践(8) --共享内存/内存映射
概述
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图)。
共享内存 VS. 其他IPC形式
用管道/消息队列传递数据
用共享内存传递数据
共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快!
mmap内存映射
将文件/设备空间映射到共享内存区
#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: 文件描述符;
offset: 从文件头开始的偏移量;
|
prot |
说明 |
|
PROT_READ |
页面可读 |
|
PROT_WRITE |
页面可写 |
|
PROC_EXEC |
页面可执行 |
|
PROC_NONE |
页面不可访问 |
|
flags |
说明 |
|
MAP_SHARED |
变动是共享的 |
|
MAP_PRIVATE |
变动是私有的 |
|
MAP_FIXED |
准确解释addr参数, 如果不指定该参数, 则会以4K大小的内存进行对齐 |
|
MAP_ANONYMOUS |
建立匿名映射区, 不涉及文件 |
mmap返回值:
成功: 返回映射到的内存区的起始地址;
失败: 返回MAP_FAILED;
内存映射示意图:
(注意: 内存映射时, 是以页面(4K)作为单位)
/** 示例1: 写文件映射
将文件以可读,可写的方式映射到进程的地址空间, 然后向其中写入内容
**/
struct Student
{
char name[4];
int age;
};
int main(int argc,char **argv)
{
if (argc != 2)
err_quit("usage: ./main <file-name>");
int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
if (fd == -1)
err_exit("file open error");
//为内存映射争取空间
if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)
err_exit("lseek error");
write(fd, "", 1);
Student *p = (Student *)mmap(NULL, sizeof(Student)*5,
PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
err_exit("mmap error");
// 此时:操纵文件就可以如同操纵内存一样了
char ch = 'a';
for (int i = 0; i < 5; ++i)
{
memcpy((p+i)->name, &ch, 1);
(p+i)->age = 20+i;
++ ch;
}
cout << "file initialized!" << endl;
if (munmap(p, sizeof(Student)*5) == -1)
err_exit("munmap error");
cout << "process exit..." << endl;
return 0;
}
/**示例2: 读文件映射
**/
int main(int argc,char **argv)
{
if (argc != 2)
err_quit("usage: ./main <file-name>");
//以只读方式打开
int fd = open(argv[1], O_RDONLY);
if (fd == -1)
err_exit("file open error");
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
Student *p = (Student *)mmap(NULL, sizeof(Student)*5,
PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
err_exit("mmap error");
// 从内存中读取数据(其实是从文件中读取)
for (int i = 0; i < 5; ++i)
{
cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;
}
if (munmap(p, sizeof(Student)*5) == -1)
err_exit("munmap error");
cout << "process exit..." << endl;
return 0;
}
map注意点:
1. 内存映射不能(也不可能)改变文件的大小;
2. 可用于进程间通信的有效地址空间不完全受限于映射文件的大小, 而应该以内存页面的大小为准(见下面测试);
3. 文件一旦被映射之后, 所有对映射区域的访问实际上是对内存区域的访问; 映射区域内容写会文件时, 所写内容不能超过文件的大小.
/** 测试: 注意点2
文件以40K的内容进行创建, 而以120K的内容进行写回
**/
//程序1: 写文件映射
int main(int argc,char **argv)
{
if (argc != 2)
err_quit("usage: ./main <file-name>");
int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);
if (fd == -1)
err_exit("file open error");
// 注意: 此处我们的文件其实只有40个字节
if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)
err_exit("lseek error");
write(fd, "", 1);
// 但是我们却是以120个字节的方式进行映射
Student *p = (Student *)mmap(NULL, sizeof(Student)*15,
PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
err_exit("mmap error");
// 以120个字节的方式进行写入
char ch = 'a';
for (int i = 0; i < 15; ++i)
{
memcpy((p+i)->name, &ch, 1);
(p+i)->age = 20+i;
++ ch;
}
cout << "file initialized!" << endl;
// 以120字节的方式卸载该内存区
if (munmap(p, sizeof(Student)*15) == -1)
err_exit("munmap error");
// 注意: 要故意暂停一会儿, 以便让read程序读取该共享内存的内容
sleep(20);
cout << "process exit..." << endl;
}
//程序2: 读文件映射
int main(int argc,char **argv)
{
if (argc != 2)
err_quit("usage: ./main <file-name>");
//以只读方式打开
int fd = open(argv[1], O_RDONLY);
if (fd == -1)
err_exit("file open error");
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
// 以120字节的方式映射
Student *p = (Student *)mmap(NULL, sizeof(Student)*15,
PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
err_exit("mmap error");
// 以120字节的方式读取
for (int i = 0; i < 15; ++i)
{
cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;
}
if (munmap(p, sizeof(Student)*15) == -1)
err_exit("munmap error");
cout << "process exit..." << endl;
}
msync函数
int msync(void *addr, size_t length, int flags);
对映射的共享内存执行同步操作
参数:
addr: 内存起始地址;
length: 长度
flags: 选项
|
flags |
说明 |
|
MS_ASYNC |
执行异步写 |
|
MS_SYNC |
执行同步写, 直到内核将数据真正写入磁盘之后才返回 |
|
MS_INVALIDATE |
使高速缓存的数据失效 |
返回值:
成功: 返回0;
失败: 返回-1;
Linux IPC实践(8) --共享内存/内存映射的更多相关文章
- Linux IPC实践(10) --Posix共享内存
1. 创建/获取一个共享内存 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #inc ...
- Linux IPC实践(1) -- 概述
进程的同步与互斥 进程同步: 多个进程需要相互配合共同完成一项任务. 进程互斥: 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥;系统中某些 ...
- Linux IPC实践(9) --System V共享内存
共享内存API #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int ...
- Linux IPC System V 共享内存
模型 #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> ftok() //获取key值 s ...
- Linux IPC实践(13) --System V IPC综合实践
实践:实现一个先进先出的共享内存shmfifo 使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速; 我们首先完成C语言版本的shmfifo(基于过程调用) ...
- Linux IPC实践(11) --System V信号量(1)
信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget ...
- Linux IPC实践(7) --Posix消息队列
1. 创建/获取一个消息队列 #include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For m ...
- Linux IPC实践(4) --System V消息队列(1)
消息队列概述 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法(仅局限于本机); 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值. 消息队列也有管道一样的不足: ...
- Linux IPC实践(2) --匿名PIPE
管道概念 管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个"管道", 管道的本质是固定大小的内核缓冲区; 如:ps aux | gre ...
随机推荐
- ConcurrentHashMap1.7和1.8的不同实现
ConcurrentHashMap 在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap,为 ...
- 浅谈Log4net在项目中如何记录日志
一 引入背景 在软件开发周期中,无论是开发中,或是测试中,或是上线后,选择合适的工具监控程序的运行状态至关重要,只有如此,才能更好地排查程序问题和检测程序性能问题等.本篇文章主要与大家分享,如何 ...
- 常用java开发文档链接
使用java开源工具httpClient及jsoup抓取解析网页数据 : https://blog.csdn.net/lovoo/article/details/52674712 jsoup Cook ...
- 打造适合你的ABP(1)---- 完善日志系统
最近使用Abp开发了一个项目,对abp有一个大概的了解,第一个小项目接近尾声,新的项目马上开始,针对开发第一个项目中发现的问题及不方便的地方,本人做一些修改,特此记录,请大家多多指正! 本人的开发环境 ...
- 20160215.CCPP体系详解(0025天)
程序片段(01):01.Malloc.c 内容概要:Malloc拓展 #include <stdio.h> #include <stdlib.h> //01.内存伸缩函数: / ...
- python用openpyxl操作excel
python操作excel方法 1)自身有Win32 COM操作office但讲不清楚,可能不支持夸平台,linux是否能用不清楚,其他有专业处理模块,如下 2)xlrd:(读excel)表,xlrd ...
- Dynamics CRM2016 Update or Create parentcustomerid in Contact using web api
联系人实体中有个特殊的字段parentcustomerid 在通过web api创建或更新记录时,如果在给这个字段赋值时当做查找字段对待的话,那你就会遇到问题了,报错信息如下 正确的赋值方式如下
- (译)Objective-C 类属性
翻译自:Objective-C Class Properties 译者:Haley_Wong 由于Swift 3.0 出了太多令人兴奋的新特性,人们很容易忽略 Objective-C中的小改动.苹果展 ...
- Android Multimedia框架总结(十四)Camera框架初识及自定义相机案例
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52738492 前言:国庆节告一段 ...
- introduction of velocity
一.velocity 简介 基于java 的模板引擎,apache 旗下的开源软件项目. 目的在于隔离 表示层和业务逻辑层,当然现在做的不仅仅是这些. 二.应用场景 web 应用程序:创建html页面 ...