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://xiahunao.cn/news/2777979.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

队列---数据结构

定义 队列&#xff08;Queue&#xff09;简称队&#xff0c;也是一种操作受限的线性表&#xff0c;只允许在表的一端进行插入&#xff0c;而在表的另一端进行删除。向队列中插入元素称为入队或进队&#xff1b;删除元素称为出队或离队。 队头&#xff08;Front&#xff09;&a…

上位机图像处理和嵌入式模块部署(利用python开发软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 开发windows和linux软件的时候&#xff0c;大家一般都是习惯于用c/c语言进行开发&#xff0c;但是目前来说很多的开发板都是支持python语言开发的。…

防火墙USG6000V配置接口实验

要求:防火墙向下使用子接口分别对应生产区和办公区,所有分区设备可以ping通网关 最终效果部分示例:client1,Server1,PC2都能ping通网关 实现流程: 云要添加虚拟网卡ip(此处用的是创建的虚拟环回ip),更改端口映射为双向通道且更改编号 SW1:#要先登录防火墙初始账号和密码,然后…

尝新果未熟,探新途未尽。寒冬凝锐气,雷鸣蓄神力——小康师兄的2023年度总结

文章目录 一、前言二、工作总结2.1 我期望的&#xff0c;而公司想要的2.2 公司利益VS员工利益2.3 这个问题问得很有问题 三、生活总结3.1 一胎3.2 二胎 四、其他总结4.1 博客4.2 无人自助台球馆4.3 我要出书了 五、OKR 一、前言 又是一年除夕夜&#xff0c;万家灯火同团圆。 老…

|Python新手小白低级教程|第十九章:函数(1)

文章目录 前言一、概说二、方法def简介1.示例&#xff1a;使用def关键字制作功能函数——找最大最小2.代码剖析示例代码Part 1示例代码Part 2示例代码Part 3练习1.1制作函数 三、灵活使用函数1.制作一种函数&#xff0c;函数名和格式为even_num(a,b)&#xff0c;输入a&#xff…

数据结构——单向链表和双向链表的实现(C语言版)

目录 前言 1. 链表 1.1 链表的概念及结构 1.2 链表的分类 2. 单链表接口实现 2.1 数据结构设计与接口函数声明 2.2 创建结点&#xff0c;打印&#xff0c;查找 2.3 尾插&#xff0c;头插&#xff0c;尾删&#xff0c;头删 2.4 插入或删除 2.4.1在指定位置后 2.4.2在…

防疫物资管理新篇章:Java+SpringBoot实战

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

初识String类和String类的拓展

前言&#xff1a;以下是Java中String类的知识点与一些常见问题和注意事项&#xff0c;如有讲解不妥&#xff0c;请见谅&#xff01; 目录 1.String类的创建及常见API &#xff08;1&#xff09;String类的四种创建方式&#xff1a; 补充&#xff1a;字符串转化成字符数组 / …

038 什么是面向对象

面向过程&面向对象 什么是面向对象 现实世界中的事物、类、对象之间的关系 在我们想通过计算机解决一个具体问题的时候&#xff0c;我们可以研究与问题有关事物的共性&#xff0c;比如我在观察了大量的杯子后得出一些结论&#xff1a;杯子都应该有材质、颜色、尺寸、形状这…

Docker 容器网络:C++ 客户端 — 服务器应用程序。

一、说明 在下面的文章中&#xff0c; 将向您概述 docker 容器之间的通信。docker 通信的验证将通过运行 C 客户端-服务器应用程序和标准“ping”命令来执行。将构建并运行两个单独的 Docker 映像。 由于我会关注 docker 网络方面&#xff0c;因此不会提供 C 详细信息。…

LeetCode-第15题-三叔之和

1.题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组…

1899_野火FreeRTOS教程阅读笔记_任务创建

1899_野火FreeRTOS教程阅读笔记_任务创建 全部学习汇总&#xff1a; g_FreeRTOS: FreeRTOS学习笔记 (gitee.com) 关于这部分&#xff0c;从一般前后台程序到RTOS的任务描述了很多。但是我觉得这本书的这部分描述没有描述到关键的信息点。其实&#xff0c;RTOS存在的一个主要的目…

MySQL篇----第八篇

系列文章目录 文章目录 系列文章目录前言一、存储过程优化思路二、触发器(一段能自动执行的程序)三、数据库并发策略四、MySQL 中有哪几种锁?五、MySQL 中有哪些不同的表格?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳…

创建自己的系统创富法则,做个轻松赚钱的甩手掌柜

一、教程描述 本套系统创富教程&#xff0c;大小744.80M&#xff0c;共有28个文件。 二、教程目录 01.走遍全球四十多个国家&#xff0c;我才发现赚钱的本质如此雷同.mp4 02.靠工资技术赚钱太慢&#xff0c;想赚到自己的第一个一百万的方法是&#xff1f;.mp4 03.不服暴发…

CSP-202112-2-序列查询新解

CSP-202112-2-序列查询新解 【70分思路】 【暴力枚举】按照题目思路遍历一遍f(x)和g(x)&#xff0c;计算error(A)&#xff0c;时间复杂度为O(N)&#xff0c;时间超限。 #include <iostream> using namespace std; int main() {long long n, N, sum 0;cin >> n …

【c++基础】骑士的金币(coin)(NOIP2015)

说明 国王将金币作为奖励&#xff0c;发放给忠诚的骑士。第一天&#xff0c;骑士收到一枚金币&#xff1b;之后两天&#xff08;第二天和第三天&#xff09;里&#xff0c;每天收到两枚金币&#xff1b;之后三天&#xff08;第四、五、六天&#xff09;里&#xff0c;每天收到…

微软 CMU - Tag-LLM:将通用大语言模型改用于专业领域

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 论文地址&#xff1a;https://arxiv.org/abs/2402.05140 Github 地址&#xff1a;https://github.com/sjunhongshen/Tag-LLM 大语言模型&#xff08…

ChatGPT高效提问—prompt常见用法

ChatGPT高效提问—prompt常见用法 1.1 角色扮演 ​ prompt最为常见的用法是ChatGPT进行角色扮演。通常我们在和ChatGPT对话时&#xff0c;最常用的方式是一问一答&#xff0c;把ChatGPT当作一个单纯的“陪聊者”。而当我们通过prompt为ChatGPT赋予角色属性后&#xff0c;即使…

SpringIOC之support模块PropertySourcesPlaceholderConfigurer

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

LeetCode Python - 6.Z字形变换

文章目录 题目答案运行结果 题目 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1a; P A H N A P L S I I G Y I R 之后&#xff0c;你的输…