Linux应用 进程间通信之共享内存(System V)

1、定义

System V共享内存是一种在Unix和类Unix操作系统上用于进程间通信的机制。它允许多个进程共享同一块物理内存区域,从而可以在这些进程之间传递数据。

应用场景:

  • 数据共享:多个进程需要共享大量数据,如数据库缓存、图像处理等。
  • 通信效率:共享内存是一种高效的通信方式,适用于需要快速传递大量数据的场景。

优点:

  • 高效:共享内存是一种高效的通信方式,因为进程可以直接访问共享的内存区域。
  • 灵活性:共享内存允许多个进程共享数据,提供了一种灵活的通信方式。

缺点:

  • 同步问题:由于多个进程可以同时访问共享内存,需要额外的同步机制来避免数据竞争和一致性问题。
  • 安全性:共享内存需要额外的安全机制来保护数据,防止其他进程非法访问。
  • 编程复杂性:使用共享内存需要处理同步和数据一致性等复杂问题,编程复杂度较高。

2、常用接口介绍

2.1 编程常用接口和数据结构

2.1.1 ftok函数

ftok函数用于生成一个System V IPC对象(如消息队列、共享内存等)的key。它将pathname和proj_id组合起来,生成一个唯一的key,用于标识一个System V IPC对象。

key_t ftok(const char *pathname, int proj_id);
  • 入参:pathname是一个路径名,proj_id是一个用户指定的整数。
  • 返回值:返回一个基于pathname和proj_id生成的key。
2.1.2 shmget函数

创建一个新的共享内存或获取一个已存在的共享内存的标识符。

int shmget(key_t key, size_t size, int shmflg);
  • 入参:key(用于标识共享内存)、size(共享内存的大小)、flags(用于指定共享内存的访问权限和创建方式)。
  • 返回值:返回共享内存的标识符(即共享内存的ID),出错时返回-1。

shmflg 选项:

  • IPC_CREAT:如果共享内存不存在,则创建一个新的共享内存段。
  • IPC_EXCL:与 IPC_CREAT 一起使用时,如果共享内存已经存在,则返回错误。
  • 权限标志:比如 IPC_PRIVATE 或者 IPC_CREAT | 0666,用于指定共享内存的访问权限
2.1.3 shmat函数

将共享内存连接到当前进程的地址空间,使得进程可以访问共享内存中的数据。

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 入参:shmid(共享内存的标识符)、shmaddr(指定共享内存连接到进程的地址)、shmflg(用于指定连接方式)。
  • 返回值:返回共享内存连接的地址,出错时返回-1。

shmflg 选项:

  • SHM_RDONLY:将共享内存连接为只读模式,进程无法对共享内存进行写操作。
  • 0:通常使用0,表示默认的连接方式。
2.1.4 shmdt函数

将共享内存从当前进程的地址空间分离,使得进程不再能够访问共享内存中的数据。

int shmdt(const void *shmaddr);
  • 入参:shmaddr(指定要分离的共享内存地址)。
  • 返回值:返回0表示成功,返回-1表示失败。
2.1.5 shmctl函数

对共享内存执行各种控制操作,比如删除共享内存、获取共享内存状态等。

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 入参:shmid(共享内存的标识符)、cmd(用于指定要执行的操作)、buf(用于传递或接收信息的结构体指针)。
  • 返回值:返回操作相关的信息,出错时返回-1。

cmd 选项:

  • IPC_STAT:获取共享内存的状态信息。
  • IPC_SET:设置共享内存的状态信息。
  • IPC_RMID:删除共享内存。
  • IPC_INFO:获取系统关于共享内存的信息。
2.1.6 struct shmid_ds
struct shmid_ds {struct ipc_perm shm_perm;  // 共享内存的权限和拥有者信息size_t          shm_segsz; // 段的大小(字节)time_t          shm_atime; // 最后一次连接时间time_t          shm_dtime; // 最后一次分离时间time_t          shm_ctime; // 最后一次改变时间pid_t           shm_cpid;  // 创建者的进程IDpid_t           shm_lpid;  // 最后一次调用shmat(2)/shmdt(2)的进程IDunsigned short  shm_nattch; // 当前连接的进程数
};struct ipc_perm {key_t          __key;    // 键值uid_t          uid;      // 所有者的用户IDgid_t          gid;      // 所有者的组IDuid_t          cuid;     // 创建者的用户IDgid_t          cgid;     // 创建者的组IDunsigned short mode;     // 权限unsigned short __seq;    // 序列号
};

2.2 控制台常用命令

2.2.1 ipcs

ipcs命令用于显示系统中的IPC资源信息,包括消息队列、共享内存和信号量。-m选项表示只显示共享内存的信息:

ipcs -m
2.2.2 ipcrm

ipcrm命令用于删除指定的 IPC 对象,包括共享内存:

ipcrm -m <semid>

3、编程示例

3.1 共享内存实现进程间通信

测试代码如下:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>#define MEM_FILE_PATH "/home/mem"
#define MEM_SIZE 1024
// 打印时分秒的宏        
#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)int main(int argc, char *argv[]) 
{key_t key;int shmid;char *data = NULL;int SendNum = 0;char DataTemp[16] = {0};// 文件不存在则创建文件if (-1 == access(MEM_FILE_PATH, F_OK)){system("touch "MEM_FILE_PATH);}// 获取keyif((key = ftok(MEM_FILE_PATH, 'a')) < 0){return 0;}       shmid = shmget(key, MEM_SIZE, IPC_CREAT | 0666);// 命令行参数 // 第一个参数 W表示每2秒写入一次数据 R表示每1秒读取数据if (argc != 2) {printf("Usage: %s W|R|D\n", argv[0]);return 0;}data = (char*)shmat(shmid, (void*)0, 0);if (!strcmp(argv[1], "W")){while(1){bzero(DataTemp, sizeof(DataTemp));bzero(data, MEM_SIZE);sprintf(DataTemp, "Data-%d", SendNum);SendNum++;strcpy(data, DataTemp);PRINT_MIN_SEC();printf("Write:%s\n",DataTemp);if(SendNum == 5){break;}sleep(2);}shmdt(data);} else if (!strcmp(argv[1], "R")) {while(1){bzero(DataTemp, sizeof(DataTemp));SendNum++;strcpy(DataTemp, data);PRINT_MIN_SEC();printf("Read:%s\n",DataTemp);if(SendNum == 10){break;}sleep(1);}shmdt(data);} else if (!strcmp(argv[1], "D")) {if(!shmctl(shmid, IPC_RMID, NULL)){printf("Delete OK\n");}} else{printf("Usage: %s W|R|D\n", argv[0]);return 0;}    
}

运行程序指定不同参数实现向共享内存进行读写:

测试删除功能:

3.2 共享内存和信号量实现进程间通信

上述3.1的示例中程序通过读取频率超多写入频率的方式保证获取到所有数据,此方法在正常编程过程中不会使用,需要通过信号量等方式实现进程间读写的同步,信号量编程可参考之前的文章:linux应用 进程间通信之信号量(System V)。

编写测试用例,使用信号量实现读写同步,测试代码如下:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>#define MEM_FILE_PATH "/home/mem"
#define SEM_FILE_PATH "/home/sem1"
#define MEM_SIZE 1024
// 打印时分秒的宏        
#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)int main(int argc, char *argv[]) 
{key_t key, key_sem;int shmid, semid;char *data = NULL;int SendNum = 0;char DataTemp[16] = {0};struct sembuf sops = {0};// 文件不存在则创建文件if (-1 == access(MEM_FILE_PATH, F_OK)){system("touch "MEM_FILE_PATH);}// 文件不存在则创建文件if (-1 == access(SEM_FILE_PATH, F_OK)){system("touch "SEM_FILE_PATH);}// 获取keyif((key = ftok(MEM_FILE_PATH, 'a')) < 0){return 0;}       // 获取keyif((key_sem = ftok(SEM_FILE_PATH, 'a')) < 0){return 0;}shmid = shmget(key, MEM_SIZE, IPC_CREAT | 0666);// 信号量[0]用于写P操作 读V操作// 信号量[1]用于写V操作 读P操作semid = semget(key_sem, 2, IPC_CREAT | 0666);if(semid < 0){perror("Failed to create semaphore");}// 命令行参数 // 第一个参数 W表示写入 R表示读取if (argc != 2) {printf("Usage: %s W|R|D\n", argv[0]);return 0;}data = (char*)shmat(shmid, (void*)0, 0);// 信号量值初始化union semun {int val;struct semid_ds *buf;unsigned short *array;} arg;arg.val = 0;semctl(semid, 0, SETVAL, arg);semctl(semid, 1, SETVAL, arg);if (!strcmp(argv[1], "W")){// 设置信号量[1]的值为0arg.val = 0;semctl(semid, 1, SETVAL, arg);while(1){// 写前执行信号量[0]P操作 等读操作发起后将信号量设置为1 然后进行写操作sops.sem_flg = 0;sops.sem_op = -1; sops.sem_num = 0;semop(semid, &sops, 1); // 向共享内存写入数据bzero(DataTemp, sizeof(DataTemp));bzero(data, MEM_SIZE);sprintf(DataTemp, "Data-%d", SendNum);SendNum++;strcpy(data, DataTemp); // 写完后执行信号量[1]V操作 通知读进程可以读取sops.sem_flg = 0;sops.sem_op = 1; sops.sem_num = 1;semop(semid, &sops, 1); PRINT_MIN_SEC();printf("Write:%s\n",DataTemp);if(SendNum == 5){break;}}shmdt(data);} else if (!strcmp(argv[1], "R")) {// 设置信号量[0]的值为1 通知写进程可以写入arg.val = 1;semctl(semid, 0, SETVAL, arg);while(1){// 读前执行信号量[1]P操作 等待写进程写完数据sops.sem_flg = 0;sops.sem_op = -1; sops.sem_num = 1;semop(semid, &sops, 1);// 从共享内存读取数据bzero(DataTemp, sizeof(DataTemp));SendNum++;strcpy(DataTemp, data);// 读后执行信号量[0]V操作 让写进程进行下一次写入sops.sem_flg = 0;sops.sem_op = 1; sops.sem_num = 0;semop(semid, &sops, 1); PRINT_MIN_SEC();printf("Read:%s\n",DataTemp);if(SendNum == 5){break;}}shmdt(data);} else if (!strcmp(argv[1], "D")) {if(!shmctl(shmid, IPC_RMID, NULL)){printf("Delete OK\n");}} else{printf("Usage: %s W|R|D\n", argv[0]);return 0;}    
}

使用两个信号量实现读写同步,不再需要时间函数,测试结果如下:

4、总结

本文阐述了进程间通信之共享内存(System V)的定义,列举了编程中使用的接口和linux命令,编写了测试用例测试相关功能。

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

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

相关文章

k8s-项目部署案例

一、容器交付流程 在k8s平台部署项目流程 在K8s部署Java网站项目 DockerFile 如果是http访问&#xff0c;需要在镜像仓库配置可信任IP 三、使用工作负载控制器部署镜像 建议至少配置两个标签 一个是声明项目类型的 一个是项目名称的 继续配置属性 资源配额 健康检查 五、使…

TS项目实战二:网页计算器

使用ts实现网页计算器工具&#xff0c;实现计算器相关功能&#xff0c;使用tsify进行项目编译&#xff0c;引入Browserify实现web界面中直接使用模块加载服务。   源码下载&#xff1a;点击下载 讲解视频 TS实战项目四&#xff1a;计算器项目创建 TS实战项目五&#xff1a;B…

零基础学编程从入门到精通,系统化的编程视频教程上线,中文编程开发语言工具构件之缩放控制面板构件用法

一、前言 零基础学编程从入门到精通&#xff0c;系统化的编程视频教程上线&#xff0c;中文编程开发语言工具构件之缩放控制面板构件用法 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例源码文件下载可以点击最下方官网卡片——软件下载—…

Win32 SDK Gui编程系列之--ListView自绘OwnerDraw(续)

通过所有者绘制的列表视图(2) 所有者绘制列表视图的基础已在前一页中说明。本页将展示如何在所有者绘制列表视图中显示数据库表数据。 1、访问日志 正如在另一个页面中所述,本网站的访问日志目前是通过SQLite3数据库管理的。 以下是上述程序执行的结果。为…

网神 SecGate 3600 防火墙 route_ispinfo_import_save 文件上传漏洞复现

0x01 产品简介 网神SecGate 3600防火墙是基于状态检测包过滤和应用级代理的复合型硬件防火墙,是专门面向大中型企业、政府、军队、高校等用户开发的新一代专业防火墙设备,支持外部攻击防范、内网安全、网络访问权限控制、网络流量监控和带宽管理、动态路由、网页内容过滤、邮…

数据库管理-第146期 最强Oracle监控EMCC深入使用-03(20240206)

数据库管理145期 2024-02-06 数据库管理-第146期 最强Oracle监控EMCC深入使用-03&#xff08;20240206&#xff09;1 概览2 性能中心3 性能中心-Exadata总结 数据库管理-第146期 最强Oracle监控EMCC深入使用-03&#xff08;20240206&#xff09; 作者&#xff1a;胖头鱼的鱼缸&…

C++初阶篇----新手进村

目录 一、什么是C二、C关键字三、命名空间3.1命名空间的定义3.2命名空间的使用 四、C输入和输出五、缺省参数5.1缺省参数的概念5.2缺省参数的分类 六、函数重载6.1函数重载的概念6.2函数重载的原理----名字修饰 七、引用7.1引用概念7.2引用特性7.3常引用7.4引用的使用7.5传值、…

波奇学Linux:文件重定向和虚拟文件系统

重定向 文件描述符所对应的分配规则&#xff0c;从0开始&#xff0c;寻找最小没有使用的数组位置。 如图所示&#xff0c;关闭文件描述符的0&#xff0c;新打开的文件描述符为0&#xff0c;而关闭2&#xff0c;文件描述符为2。 重定向&#xff1a;文件输出的对象发生改变 例…

肯尼斯·里科《C和指针》第12章 使用结构和指针(1)链表

只恨当时学的时候没有读到这本书&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; 12.1 链表 有些读者可能还不熟悉链表&#xff0c;这里对它作一简单介绍。链表(linked list)就一些包含数据的独立数据结构&#xff08;通常称为节点&#xff09;的集…

代码审计-CVE-2023-6654-PHPEMS-加密-解密分析

路由&#xff1a; 入口方法&#xff1a; 鉴权分析&#xff1a; 由此可以得出 鉴权是由session类负责获取参数后&#xff0c;由各个类的魔术方法负责&#xff1a;&#xff08;在此还有一个方法 全局搜索登录关键词&#xff09; 1、断点分析&#xff1a; 寻找鉴权点分析&#…

(十八)springboot实战——spring securtity注解方式的授权流程源码解析

前言 在上一节内容中&#xff0c;我们介绍了如何在FilterSecurityInterceptor过滤器中处理用户的授权流程&#xff0c;并分析了其源码&#xff0c;spring security还提供了方法级别的授权方式&#xff0c;通过EnableMethodSecurity注解启用权限认证流程&#xff0c;只需要在方…

SpringBoot 事务管理Transactional 数据回滚 数据一致性

SpringBoot 事务的属性rollbackFor 与 propagetion 介绍 SpringBoot当中的事物他保证了一致性&#xff0c;要么全部一起成功&#xff08;提交&#xff09;&#xff0c;要么一起失败&#xff0c;失败&#xff08;回滚&#xff09;后数据会回到当初的样子&#xff0c;是一组操…

No matching client found for package name ‘com.unity3d.player‘

2024年2月6日更新 使用Unity插件包方式接入&#xff08;Unity方式&#xff09;&#xff0c;且必须使用EDM(ExternalDependencyManager)&#xff0c;若已有ExternalDependencyManager文件夹&#xff08;其他SDK也可能带有&#xff09;则选用最新的即可。 工程Assets- E…

Qt网络编程-写一个简单的网络调试助手

环境 Windows&#xff1a;Qt5.15.2&#xff08;VS2022&#xff09; Linux&#xff1a;Qt5.12.12&#xff08;gcc) 源代码 TCP服务器 头文件&#xff1a; #ifndef TCPSERVERWIDGET_H #define TCPSERVERWIDGET_H #include <QWidget> namespace Ui { class TCPServerW…

Linux文件和目录管理

目录基础 Linux操作系统以目录的方式来组织和管理系统中的所有文件。所谓的目录&#xff0c;就是将所有文件的说明信息采用树状结构组织起来。每个目录节点之下会有文件和子目录。 所有一切都从 ‘根’ 开始&#xff0c;用 ‘/’ 代表, 并且延伸到子目录。 bin&#xff1a;B…

【RT-DETR有效改进】利用SENetV1重构化网络结构 (ILSVRC冠军得主)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、本文介绍 本文给大家带来的改进机制是SENet(Squeeze-and-Excitation Networks)其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型,而是一个可以和现有的任何一个模型相结合…

R语言学习case10:ggplot基础画图Parallel Coordinate Plot 平行坐标图

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 plot5 <- ggparcoord(…

【ArcGIS微课1000例】0101:删除冗余节点或折点

文章目录 一、实验描述二、实验数据三、实验过程1. 手动删除2. 简化线工具四、注意事项一、实验描述 矢量数据获取通常来源于手动或者ArcScan自动采集,其基本存储方式就是记录每个要素的点坐标,如点要素就是一个坐标、线要素由多个点要素连接形成。当某段线要素被过多的节点…

单片机学习笔记---蜂鸣器工作原理

目录 蜂鸣器介绍 蜂鸣器的驱动方式 ULN2003D芯片工作原理 实战预备知识&#xff1a;基础乐理 音名的分组 全音和半音的关系 音高的表示 五线谱中的符号定义 简谱上的符号定义 C调音符与频率对照表 相关计算 蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件&a…

SaperaCamExpert(相机专家)中文使用指南

参考&#xff1a;SaperaCamExpert中文使用指南.PDF 文章目录 软件介绍安装首次打开资源占用率功能主界面布局菜单栏FileViewPre-Processing&#xff1a;预处理 Tools&#xff1a; 快捷键&#xff1a;新建&#xff1b;打开&#xff1b;保存&#xff1b;帮助Device窗体属性树图像…