进程间通信-POSIX 共享内存
POSIX 共享内存
POSIX 共享内存是一种在 Linux 系统上使用的共享内存机制,它允许多个进程可以访问同一个内存区域,从而实现进程间的数据共享。共享内存是可用IPC机制中最快的,使用共享内存不必频繁拷贝数据。但也需要注意,由于共享内存段中的数据可以被多个进程同时访问,因此需要在程序设计中考虑好数据同步和互斥机制,以避免出现数据竞争和不一致的情况。
共享内存使用的基本步骤:
- 通过 shm_open() 函数创建了共享内存区域,此时会在 /dev/shm/ 创建共享内存文件。
- 通过 ftruncate() 函数改变共享内存的大小,一般设置为页大小 sysconf(_SC_PAGE_SIZE) 的整数倍。
- 通过 mmap() 函数将创建的共享内存文件映射到内存。
- 通过 munmap() 卸载共享内存。
- 通过 shm_unlink() 删除内存共享文件。
下面分别介绍这些函数。
创建共享内存-shm_open() 函数
shm_open() 函数是用于创建或打开一个共享内存对象,该函数定义如下:
#include <sys/mman.h> int shm_open(const char *name, int oflag, mode_t mode);
参数说明
- name:指定共享内存对象的名称,其命名规则类似于文件系统中的文件名,不同进程可以通过相同的名字来访问同一个共享内存对象。
- oflag:指定打开方式。
- mode:创建文件时的权限。
返回值
- 如果函数执行成功,返回一个文件描述符,可以用于后续操作共享内存对象。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
共享内存对象通过IPC名字描述,当成功创建共享内存对象,系统会以文件形式将其保存在 /dev/shm 目录下。
更改文件大小-ftruncate() 函数
ftruncate() 函数用于更改文件大小,该函数定义如下:
#include <unistd.h> int ftruncate(int fd, off_t length);
参数说明
- fd:一个已经打开的文件描述符,用于指定需要更改大小的文件。
- length:指定文件应当调整到的新大小,单位是字节。
返回值
- 如果函数执行成功,返回值为0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
如果文件在调整大小后比原来更大,文件的数据将会被扩展,多出的部分以0填充。如果文件在调整大小后比原来更小,多出的部分将会被删除。
在调整文件大小时,尽量选择当前系统页大小的整数倍,可以通过 sysconf(_SC_PAGE_SIZE) 获取当前系统的页大小。
内存映射-mmap()函数
mmap() 函数用于创建内存映射区域,该函数定义如下:
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot,
int flags, int fd, off_t offset);
参数说明
- addr:指定希望内存映射区域的起始地址,通常设为 NULL,由系统自动选择合适的地址。
- length:指定需要映射的内存区域的长度,单位是字节。
- prot:用于设置内存区域的保护权限,不能与文件的打开模式冲突。可以是如下值的组合:
- PROT_NONE:内存区域不可访问。
- PROT_WRITE:内存区域可以被写入。
- PROT_READ:内存区域可以被读取。
- PROT_EXEC:内存区域可以被执行。
- flags:指定映射对象的类型。下面列出一些常用项:
- MAP_SHARED:与文件进行共享映射,可以实现多个进程之间共享数据的操作。对映射区域的修改会反映到文件中,同样文件的修改也会反映到映射区域中。
- MAP_PRIVATE:创建一个私有的映射副本,进程之间不共享数据。对映射区域的修改不会反映到文件中,也不会影响其他映射该文件的进程。
- MAP_LOCKED:映射区域会被锁定在物理内存中,防止页面被交换出去,保证内存访问速度,但可能会导致内存资源消耗较大。
- fd:已打开文件的文件描述符,用于与内存映射区域关联。
- offset:文件中的偏移量,指定文件的起始映射位置。
返回值
- 如果函数执行成功,返回一个指向映射区域的指针。
- 如果发生错误,返回 MAP_FAILED(-1) 。可以通过 errno 变量来获取具体的错误信息。
映射方式如下:
数据同步-msync()函数
如果指定了 MAP_SHARED 标志,Linux内核会保持内存映射文件与内存映射区的同步,但这种同步可能不会立即生效,此时,可以使用 msync() 函数来确保数据已经同步完成。该函数定义如下:
#include <sys/mman.h> int msync(void *addr, size_t length, int flags);
参数说明
- addr:指向共享内存区域的指针。
- length:需要同步的共享内存区域的长度。
- flags:用来指定额外的行为。有如下取值:
- MS_SYNC:强制将修改同步到存储设备,数据量大时,可能会导致阻塞。
- MS_ASYNC:将修改排入写队列,不会等待写操作完成。
- MS_INVALIDATE:标记共享内存区域已经无效,使下次访问该区域时重新从底层存储器加载数据。
返回值
- 如果函数执行成功,函数返回 0。
- 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。
注意事项
- msync() 函数的调用可能会比较耗时,因此需要考虑性能问题。
- msync() 函数只适用于共享内存,对于普通的内存操作并不适用。
卸载共享内存-munmap()函数
munmap() 函数用于取消指定的地址空间内存映射。该函数定义如下:
#include <sys/mman.h> int munmap(void *addr, size_t length);
参数说明
- addr:要取消映射的内存区域的起始地址。是由 mmap() 函数返回的地址。
- length:要取消映射的内存区域的长度。
返回值
- 如果函数执行成功,函数返回 0。
- 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。
注意事项
- 只能取消由 mmap() 函数创建的内存映射区域,否则会导致未定义行为。
- 要确保取消映射的地址和长度是有效的,否则会导致程序崩溃或内存泄漏。
- 取消映射后,原先映射的内存区域就会被释放,程序不应再访问这部分内存。
删除共享内存文件-shm_unlink()函数
shm_unlink()函数用于删除共享内存文件,后续其他进程将无法通过这个名称打开该共享内存对象。该函数定义如下:
#include <sys/mman.h> int shm_unlink(const char *name);
参数说明
- name:要删除的 POSIX 共享内存对象的名称。
返回值
- 如果函数执行成功,函数返回 0。
- 如果函数执行失败,函数返回 -1,可以通过 errno 变量来获取具体的错误信息。
用例
数据同步
1 #include<stdio.h>
2 #include<fcntl.h>
3 #include<sys/mman.h>
4 #include<unistd.h>
5 #include<sys/stat.h>
6
7 int main(int argc, char** argv)
8 {
9 size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
10 int fd;
11
12 AGAIN:
13 fd = shm_open("/my_shm", O_CREAT | O_RDWR | O_EXCL, 0666);
14 if(fd == -1)
15 {
16 if(errno == EEXIST)
17 {
18 shm_unlink("/my_shm");
19 goto AGAIN;
20 }
21 perror("shm_open");
22 return -1;
23 }
24
25 int ret = ftruncate(fd, mem_size);
26 if(ret == -1)
27 {
28 perror("ftruncate");
29 return -1;
30 }
31
32 void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
33 if(ptr == MAP_FAILED)
34 {
35 perror("mmap");
36 return -1;
37 }
38
39 int i = 0;
40 sprintf(ptr, "Data%d", i);
41 while(1);
42
43 close(fd);
44 munmap(ptr, mem_size);
45 return 0;
46 }
输出:
$ cat /dev/shm/my_shm
Data1
当指定 MAP_SHARED 后,对指针 ptr 的操作,都会同步至 my_shm 文件中,数据是以明文形式存储,可以被其他进程读取。
如果将 MAP_SHARED 改为 MAP_PRIVATE,执行结果如下:
$ cat /dev/shm/my_shm
//无数据
简单用例
读端代码如下:
1 #include<stdio.h>
2 #include<fcntl.h>
3 #include<sys/mman.h>
4 #include<unistd.h>
5 #include<sys/stat.h>
6 #include<errno.h>
7
8 int main(int argc, char** argv)
9 {
10 size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
11 int fd = shm_open("/my_shm", O_CREAT | O_TRUNC | O_RDWR, 0666);
12 if (fd == -1 && errno != EEXIST)
13 {
14 perror("shm_open");
15 return -1;
16 }
17
18 int ret = ftruncate(fd, mem_size);
19 if(ret == -1)
20 {
21 perror("ftruncate");
22 return -1;
23 }
24
25 void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
26 if(ptr == MAP_FAILED)
27 {
28 perror("mmap");
29 return -1;
30 }
31
32 while (1)
33 {
34 printf("read data : %s\n", (char *)ptr);
35 sleep(1);
36 }
37
38 close(fd);
39
40 return 0;
41 }
写端代码如下:
1 #include<stdio.h>
2 #include<fcntl.h>
3 #include<sys/mman.h>
4 #include<unistd.h>
5 #include<errno.h>
6 #include<sys/stat.h>
7
8 int main(int argc, char** argv)
9 {
10 size_t mem_size = sysconf(_SC_PAGE_SIZE) * 2;
11 int fd;
12 AGAIN:
13 fd = shm_open("/my_shm", O_CREAT | O_TRUNC | O_RDWR, 0666);
14 if (fd == -1)
15 {
16 if (errno == EEXIST)
17 {
18 goto AGAIN;
19 }
20 perror("shm_open");
21 return -1;
22 }
23
24 int ret = ftruncate(fd, mem_size);
25 if(ret == -1)
26 {
27 perror("ftruncate");
28 return -1;
29 }
30
31 void *ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
32 if(ptr == MAP_FAILED)
33 {
34 perror("mmap");
35 return -1;
36 }
37
38 int i = 0;
39 while(1)
40 {
41 sprintf(ptr, "Data%d", i++);
42 printf("write data : %s\n", (char*)ptr);
43 sleep(1);
44 }
45
46 close(fd);
47 munmap(ptr, mem_size);
48
49 return 0;
50 }
进程间通信-POSIX 共享内存的更多相关文章
- 进程间通信--POSIX共享内存
1.参考:https://www.cnblogs.com/Anker/archive/2013/01/19/2867696.html
- Linux环境进程间通信(五): 共享内存(下)
linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...
- Linux环境进程间通信(五): 共享内存(上)
linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...
- Linux IPC实践(10) --Posix共享内存
1. 创建/获取一个共享内存 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #inc ...
- Linux进程间通信(四) - 共享内存
共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只 ...
- 浅析Linux下进程间通信:共享内存
浅析Linux下进程间通信:共享内存 共享内存允许两个或多个进程共享一给定的存储区.因为数据不需要在客户进程和服务器进程之间复制,所以它是最快的一种IPC.使用共享内存要注意的是,多个进程之间对一给定 ...
- POSIX共享内存
DESCRIPTION 共享内存是最快的可用IPC形式.它允许多个不相关(无亲缘关系)的进程去访问同一部分逻辑内存. 如果需要在两个进程之间传输数据,共享内存将是一种效率极高的解决方案.一旦这样的内存 ...
- POSIX 共享内存和 系列函数
在前面介绍了system v 共享内存的相关知识,现在来稍微看看posix 共享内存 和系列函数. 共享内存简单来说就是一块真正的物理内存区域,可以使用一些函数将这块区域映射到进程的地址空间进行读写, ...
- Posix共享内存区
1.概述 Posix提供了两种在无亲缘关系进程间共享内存区的方法: (1)内存映射文件:先有open函数打开,然后调用mmap函数把得到的描述符映射到当前进程地址空间中的一个文件(上一篇笔记所用到的就 ...
- 细说linux IPC(四):posix 共享内存
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途] 上一节讲了由open函数打开一 ...
随机推荐
- MDK Debug时No target connected,STM32 ST-LINK Utility连接不上单片机的解决办法“Can not connect to target!”
芯片下载程序成功,再次下载时出现,以下错误. 点击确认后,如下提示. 或提示如下. 不管怎么设置都侦测不到芯片. 使用STM32 ST-LINK Utility连接单片机时提示下边错误 "C ...
- LINUX 服务器安装nginx redis jdk等步聚
1.安装指令步聚 sudo yum update 更新linux系统 yum install -y nginx 安装nginx systemctl enable nginx 设置开机启动nginx s ...
- Springboot 在项目启动时将数据缓存到全局变量
有写字典数据不会频繁更新,但是会频繁查询,想要减少数据库链接次数,把内容缓存到项目的全局变量中,提高方法查询速度 import javax.annotation.PostConstruct; impo ...
- MSBuild属性
MSBuild 属性 MSBuild属性是键值对的集合,提前声明好这些属性之后,整个项目的生成都可以引用这些属性. 属性名不区分大小写. 属性都是写在 PropertyGroup 标签中. 1.声明属 ...
- Qt关于使用QSqlQuary::size()这个函数值返回是-1
QSqlQuary::size( ) 今天做项目的时候,用Qt连接Oracle数据库,前面都是连接成功,但是用SQL语句去操作数据库的时候,发现老是读不到内容,卡了好久. QSqlQuery Rule ...
- linux中如何判断一个rpm是手动安装还是通过yum安装的
现状 对于一个不熟悉的服务器或者是虽然是自己的服务器,但历史比较久远,对于上面安装了的一些软件包,我们记忆都慢慢模糊了. 我今天遇到一个情况,在安装一个工具x2openEuler时,安装失败,提示依赖 ...
- nnUNet 使用方法
首先明确分割任务. 其次明确研究方法和步骤. 再做好前期准备,如数据集的采集.标注以及其中的训练集/测试集划分. 其中的参考链接: (四:2020.07.28)nnUNet最舒服的训练教程(让我的奶奶 ...
- JMeter BeanShell 获取 HTTP Request 中的 Name
场景:添加 JMeter log 输出,想输入自定义请求的名称 // 获取 response body prev.getResponseDataAsString(); // 获取 HTTP Reque ...
- dockerfile 由于公钥不可用,无法验证以下签名
报错 当我在打包 docker镜像时,发生了报错 $ sudo docker build -t dcgm-exporter:3.2.5 . 1.772 The following signatures ...
- Golang 入门 : Go语言介绍
简介 Go 语言又称 Golang,由 Google 公司于 2009 年发布,近几年伴随着云计算.微服务.分布式的发展而迅速崛起,跻身主流编程语言之列,和 Java 类似,它是一门静态的.强类型的. ...