linux应用 进程间通信之共享内存(POSIX)

1、前言

1.1 定义

POSIX共享内存是一种在UNIX和类UNIX系统上可用的进程间通信机制。它允许多个进程共享同一块内存区域,从而可以在这块共享内存上进行读写操作。

1.2 应用场景

POSIX共享内存适用于需要高效地进行大量数据交换的场景,比如多个进程需要共享大型数据集合或缓存。它可以提供比其他进程间通信方式更快的数据传输速度

1.3 优缺点

1.3.1 优点
  • 高效性:共享内存允许多个进程直接访问同一块内存,因此数据传输速度更快。
  • 灵活性:由于共享内存是直接映射到进程的地址空间,因此对数据的访问更加灵活。
1.3.2 缺点
  • 同步问题:需要额外的同步机制来确保多个进程对共享内存的访问是安全的。
  • 通信复杂性:共享内存通常需要与其他进程间通信方式(如信号量、互斥量等)结合使用。

2、常用接口

2.1 shm_open函数

创建或打开一个POSIX共享内存对象。

int shm_open(const char *name, int oflag, mode_t mode);

参数

  • name:共享内存对象的名称,以斜杠开头,类似于文件路径。
  • oflag:打开标志,可以使用 O_CREAT 表示创建共享内存对象,O_RDWR 表示读写模式等。
  • mode:创建共享内存对象时的权限模式。

返回值

  • 成功:返回一个非负整数,表示共享内存对象的文件描述符。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.2 ftruncate函数

设置共享内存对象的大小。

int ftruncate(int fd, off_t length);

参数

  • fd:共享内存对象的文件描述符。
  • length:要设置的共享内存大小。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.3 mmap函数

将共享内存对象映射到进程的地址空间。

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

参数

  • addr:指定映射的地址,通常为0表示由系统选择合适的地址。
  • length:映射的长度。
  • prot:映射的保护模式,如 PROT_READ 和 PROT_WRITE。
  • flags:映射的标志,如 MAP_SHARED 表示共享映射。
  • fd:共享内存对象的文件描述符。
  • offset:共享内存对象的偏移量。

返回值

  • 成功:返回映射区的起始地址。
  • 失败:返回 MAP_FAILED,并设置 errno 以指示错误原因。

2.4 munmap函数

解除共享内存的映射。

int munmap(void *addr, size_t length);

参数

  • addr:要解除映射的地址。
  • length:映射的长度。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.5 shm_unlink函数

删除共享内存对象。

int shm_unlink(const char *name);

参数

  • name:共享内存对象的名称。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

3、编程测试

3.1 共享内存简单测试

测试代码如下,代码编译需要加-lrt -pthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \time_t t = time(NULL); \struct tm *tm_ptr = localtime(&t); \printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\} while (0);printf#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024void parent_process();
void child_process();int main() 
{pid_t pid = fork();if (pid == 0) {child_process();} else if (pid > 0) {parent_process();} else {perror("fork failed");exit(EXIT_FAILURE);}return 0;
}void parent_process() 
{PRINT_MIN_SEC("Parent start!\n");int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}ftruncate(shm_fd, SHM_SIZE);char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed");exit(EXIT_FAILURE);}bzero(ptr, SHM_SIZE);strcpy(ptr, "Parent Write Data");sleep(2); // 等待子进程读取共享内存中的数据PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);munmap(ptr, SHM_SIZE);close(shm_fd);shm_unlink(SHM_NAME);
}void child_process() 
{PRINT_MIN_SEC("Child start!\n");sleep(1); // 等待父进程创建共享内存并写入数据到共享内存int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed");exit(EXIT_FAILURE);}PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);bzero(ptr, SHM_SIZE);strcpy(ptr, "Child Write Data");munmap(ptr, SHM_SIZE);close(shm_fd);
}

通过父子间进程读写共享内存的时间不一致来实现进程通信,测试结果如下:

3.2 共享内存配合信号量使用测试

测试代码如下,代码编译需要加-lrt -pthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <semaphore.h>// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \time_t t = time(NULL); \struct tm *tm_ptr = localtime(&t); \printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\} while (0);printf#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024void parent_process();
void child_process();// 打印当前信号量的值
void printfValue(sem_t *sem)
{// 打印当前值int value;sem_getvalue(sem, &value);PRINT_MIN_SEC("Current value of semaphore: %d\n", value);
}int main() 
{sem_t *mutex = sem_open("/mysemaphore", O_CREAT, 0644, 0);  pid_t pid = fork();if (pid == 0) {child_process(mutex);} else if (pid > 0) {parent_process(mutex);} else {perror("fork failed");exit(EXIT_FAILURE);}return 0;
}void parent_process(sem_t * mutex) 
{int SendNum = 0;char SendData[32] = {0};PRINT_MIN_SEC("Parent start!\n");printfValue(mutex);int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}ftruncate(shm_fd, SHM_SIZE);char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed");exit(EXIT_FAILURE);}while(1){sprintf(SendData, "Parent Write Data-%d\n", SendNum);SendNum++;strcpy(ptr, SendData);sem_post(mutex);sleep(2);}PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);munmap(ptr, SHM_SIZE);close(shm_fd);shm_unlink(SHM_NAME);
}void child_process(sem_t * mutex) 
{PRINT_MIN_SEC("Child start!\n");printfValue(mutex);sleep(1); // 等待父进程创建共享内存并写入数据到共享内存int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (ptr == MAP_FAILED) {perror("mmap failed");exit(EXIT_FAILURE);}while(1){sem_wait(mutex);PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);}munmap(ptr, SHM_SIZE);close(shm_fd);
}

父进程通过信号量,每1秒写一次数据,写入完毕后执行post,子进程一直阻塞等待,获取到信号量后从共享内存读取数据,测试结果如下:

4、总结

本文阐述了进程间通信之共享内存(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/468614.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE】----SpringBoot的创建和使用

目录 1.Spring与SpringBoot的区别&#xff08;面试&#xff09; 2. SpringBoot的创建 3.SpringBoot创建时的问题及解决 4.SpringBoot的目录学习 5.创建一个SpringBoot 项目并且启动 1.Spring与SpringBoot的区别&#xff08;面试&#xff09; Spring 的诞⽣是为了简化 Java 程…

2023年全国职业院校技能大赛软件测试赛题第5套

2023年全国职业院校技能大赛 软件测试赛题第5套 赛项名称&#xff1a; 软件测试 英文名称&#xff1a; Software Testing 赛项编号&#xff1a; GZ034 归属产业&#xff1a; 电子与信息大类 …

如何生成狗血短剧

如何生成狗血短剧 狗血短剧剧本将上述剧本转成对话 狗血短剧剧本 标题&#xff1a;《爱的轮回》 类型&#xff1a;现代都市爱情短剧 角色&#xff1a; 1. 林晓雪 - 女&#xff0c;25岁&#xff0c;职场小白&#xff0c;善良单纯 2. 陆子轩 - 男&#xff0c;28岁&#xff0c;公…

内网穿透 | 推荐两个免费的内网穿透工具

目录 1、简介 2、Ngrok 2.1、下载安装 2.2、运行 2.3、固定域名 2.4、配置多服务 3、cpolar 3.1、下载安装 3.2、运行 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应…

【每日一题】尾随零

尾随零 目录 思路&#xff1a;代码实现&#xff1a; 思路&#xff1a; 最开始看到这题就只想到规规矩矩的做题&#xff0c;先算阶乘在算0&#xff0c;后来提交时总是提示溢出&#xff0c;不死心&#xff0c;改来改去最后没招了。 后来看题解才知道要看5的个数&#xff01; …

【AI视野·今日CV 计算机视觉论文速览 291期】Wed, 17 Jan 2024

AI视野今日CS.CV 计算机视觉论文速览 Wed, 17 Jan 2024 Totally 182 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers MultiPLY: A Multisensory Object-Centric Embodied Large Language Model in 3D World Authors Yining Hong, Zishuo Zhe…

【每日一题】牛客网——链表的回文结构

✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&#xff0c;相互学习…

动态水印怎么加 怎么去除动态水印 视频剪辑软件 会声会影安激活序列号 会声会影怎么剪辑视频

为了防止白嫖或者增加美观效果&#xff0c;视频制作者可能会采用动态水印的方式&#xff0c;让其他人难以盗取视频使用。动态水印的添加&#xff0c;需要应用到运动路径功能。接下来&#xff0c;本文会教大家动态水印怎么加&#xff0c;怎么去除动态水印的相关内容。感兴趣的小…

对进程与线程的理解

目录 1、进程/任务&#xff08;Process/Task&#xff09; 2、进程控制块抽象(PCB Process Control Block) 2.1、PCB重要属性 2.2、PCB中支持进程调度的一些属性 3、 内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 4、线程&#xff08;Thread&#xff09;…

掌握C语言文件操作:从入门到精通的完整指南!

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 什么是文件 文件其实是指一组相关数据的有序集合。这个数据集有一个名称&a…

PR:序列的设置

新建序列 序列设置 将视频拖到时间轴上&#xff0c;如果视频的分辨率或帧率和序列设置不一致会有如下提示&#xff0c;保持现有设置即可

Matplotlib Figure与Axes速成:核心技能一网打尽

Matplotlib Figure与Axes速成&#xff1a;核心技能一网打尽 &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333; 一、Figure&#xff08;图形&#xff09;&#x1f333;&#x1f341;1. 创建Figure&#x1f341;&#x1f341;2. 添加Axes&#…