Linux 进程间通信之共享内存

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:Linux知识分享⏪

🚚代码仓库:Linux代码练习🚚

🌹关注我🫵带你学习更多Linux知识
  🔝 


目录

​编辑​

前言

共享内存直接原理 

共享内存数据结构 

 共享内存函数接口

 shmget

函数原型

参数说明:

返回值:

系统限制:

额外说明:

ftok

shmat

shmdt

函数原型

参数说明

返回值

附加说明

错误代码

shmctl

 代码实现

获得key 

创建共享内存 

 挂接

 取消挂接

 删除共享内存

 共享内存命令行操作


前言

前面讲的匿名和命名都是基于文件看到同一份资源,那有没有不通过文件可以看到同一份资源?有的系统 V

共享内存直接原理 

  • 每个进程都有属于自己的进程地址空间。
  • 操作系统在物理内存中开辟一段空间,一个进程创建一段虚拟内存,并将这段虚拟内存的起始与结束地址通过页表映射到物理内存的空间。
  • 如果另一个进程也通过页表映射到同一段物理内存,那么就可以实现了让多个进程看到同一段空间,当一个进程向这段物理空间写入数据,另一个进程就可以马上从这段空间读取数据,实现进程间的通信。

共享内存需不需要被管理?当然需要啊,又不是只有这一个共享内存,其他进程万一也需要通信。共享内存多了当然需要管理。那怎么管理?老规矩先描述再组织!!!

共享内存数据结构 

struct shmid_ds {
        struct ipc_perm shm_perm;         /* operation perms */
        int shm_segsz;         /* size of segment (bytes) */
        __kernel_time_t shm_atime;         /* last attach time */
        __kernel_time_t shm_dtime;         /* last detach time */
        __kernel_time_t shm_ctime;         /* last change time */
        __kernel_ipc_pid_t shm_cpid;         /* pid of creator */
        __kernel_ipc_pid_t shm_lpid;         /* pid of last operator */
        unsigned short shm_nattch;         /* no. of current attaches */
        unsigned short shm_unused;         /* compatibility */
        void *shm_unused2;         /* ditto - used by DIPC */
        void *shm_unused3;         /* unused */
};

描述出来以后,对共享内存的管理,变成了对shmid_ds增删查改!!! 

 共享内存函数接口

 shmget

shmget 函数是 Linux 系统中用于创建或获取共享内存段标识符的函数。以下是 shmget 函数的详细解析:

函数原型

#include <sys/ipc.h>

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

参数说明:

  1. key:一个标识符,用于唯一确定系统中的共享内存段。它可以是:

    • IPC_PRIVATE:一个特殊的键值,用于创建新的共享内存段。
    • 由 ftok 函数生成的键值,通常基于文件名和项目 ID。
  2. size:要创建的共享内存段的大小,以字节为单位。实际创建的共享内存大小将是 size 向上取最近的 PAGE_SIZE(页面大小)的倍数。

  3. shmflg:共享内存的访问权限和控制标志,可以是以下选项的组合:

    • IPC_CREAT:如果共享内存不存在,则创建它。
    • IPC_EXCL:与 IPC_CREAT 一起使用,如果共享内存已存在,则 shmget 调用失败。
    • SHM_R 或 SHM_RDONLY:共享内存段为只读。
    • SHM_W:共享内存段为可写。

返回值:

  • 成功:返回共享内存段的标识符(shmid)。
  • 失败:返回 -1,并设置全局变量 errno 以指示错误类型。

错误代码:

  • EINVALsize 小于 SHMMIN 或大于 SHMMAX
  • EEXISTshmflg 包含 IPC_CREAT | IPC_EXCL,但已存在与 key 相匹配的共享内存。
  • EIDRM:尝试获取的共享内存已经被删除。
  • ENOENTkey 不存在,且 shmflg 没有设置 IPC_CREAT
  • EACCES:没有足够的权限访问指定的共享内存。

系统限制:

  • SHMALL:系统范围内共享内存段的总数限制。
  • SHMMAX:单个共享内存段的最大大小限制。
  • SHMMNI:系统范围内共享内存段标识符的最大数量限制。

额外说明:

  • 使用 shmget 创建共享内存时,通常会结合 shmctlshmat 和 shmdt 函数来完成共享内存的控制、映射、使用和脱离。
  • IPC_PRIVATE 作为 key 时,会忽略 shmflg 的低9位,并创建一个新的共享内存段。

ftok

ftok 是一个UNIX系统调用,用于创建一个唯一的键值,这个键值可以用于ftok函数创建的两个进程之间的通信。它通常用于初始化或识别一个共享内存段或信号量。

函数原型如下:

key_t ftok(const char *pathname, char proj_id);

参数说明:

  • pathname:一个字符串,指定了一个文件的路径。这个文件的存在并不是必需的,但是它必须存在于文件系统中。
  • proj_id:一个字符,用于进一步区分同一个文件下生成的不同键值。

返回值:

  • 成功时返回一个key_t类型的键值,这个键值可以用于IPC(进程间通信)。
  • 失败时返回-1,并设置errno以指示错误类型。

ftok函数通过文件名和项目标识符生成一个键值,这个键值可以用来创建或访问共享内存段或信号量。ftok函数的实现通常依赖于文件的设备号和文件名的i节点号,以及项目标识符,来生成一个唯一的键值。

请注意,ftok函数是POSIX标准的,主要用于UNIX和类UNIX系统。在Windows系统中,该函数不可用。


shmat

shmat 是一个用于进程间通信(IPC)的系统调用函数,它是System V共享内存接口的一部分。shmat 函数用于将一个已经创建的共享内存段附加(attach)到调用进程的地址空间中。一旦附加,进程就可以像访问普通内存一样访问共享内存。

函数原型如下:

void *shmat(int shm_id, const void *shmaddr, int shmflg);

参数说明:

  1. shm_id:共享内存段的标识符,通常由 shmget 函数创建并返回。
  2. shmaddr:(可选)一个指针,指定了共享内存段在调用进程地址空间中附加的位置。如果设置为NULL,系统会找到一个合适的地址来附加共享内存。
  3. shmflg:控制共享内存附加行为的标志。常见的标志有:
    • SHM_RDONLY:以只读方式附加共享内存。
    • 0:以读写方式附加共享内存。

返回值:

  • 成功时返回一个指向共享内存段的指针,进程可以使用这个指针来访问共享内存。
  • 失败时返回-1,并设置全局变量 errno 以指示错误类型。

使用 shmat 时,需要确保已经包含了相应的头文件,如 <sys/ipc.h><sys/shm.h>


shmdt

shmdt 函数是用于断开与共享内存段的连接的Linux系统调用。当一个进程通过shmat函数将共享内存段映射到自己的地址空间后,如果不再需要访问该共享内存段,就可以使用shmdt函数来断开这种映射关系。以下是shmdt函数的一些关键信息:

函数原型

int shmdt(const void *shmaddr);

参数说明

  • shmaddr:共享内存段连接到当前进程地址空间的起始地址。

返回值

  • 成功:返回0。
  • 出错:返回-1,并且errno会被设置为相应的错误代码。

附加说明

  • 本函数调用并不删除所指定的共享内存段,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)当前进程。
  • 如果shmaddr参数无效,将返回错误。

错误代码

  • EINVAL:无效的参数shmaddr

 


shmctl

shmctl 是一个用于进程间通信(IPC)的系统调用函数,它是System V共享内存接口的一部分。shmctl 函数用于控制共享内存段的各种操作,如获取共享内存段的信息、设置共享内存段的属性或删除共享内存段。

函数原型如下:

int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

参数说明:

  1. shm_id:共享内存段的标识符,通常由 shmget 函数创建并返回。
  2. cmd:一个命令,指定了要执行的操作。常见的命令有:
    • IPC_STAT:获取共享内存段的状态信息,并将信息存储在 buf 指向的结构体中。
    • IPC_SET:设置共享内存段的属性,buf 指向的结构体包含了新的属性值。
    • IPC_RMID:删除共享内存段,释放相关资源。
  3. buf:一个指向 shmid_ds 结构体的指针,该结构体用于存储共享内存段的信息或新的属性设置。

返回值:

  • 成功时返回0。
  • 失败时返回-1,并设置全局变量 errno 以指示错误类型。

shmid_ds 结构体通常包含以下成员:

  • shm_perm:共享内存段的权限。
  • shm_segsz:共享内存段的大小。
  • shm_atime:最后访问时间。
  • shm_dtime:最后删除时间。
  • shm_ctime:最后修改时间。
  • shm_cprid:最后执行读操作的进程的进程ID。
  • shm_lprid:最后执行写操作的进程的进程ID。

使用 shmctl 时,需要确保已经包含了相应的头文件,如 <sys/ipc.h><sys/shm.h>

 代码实现

我们要创建共享内存,首先要有key而key是调用ftok来获得,我们先创建一个comm.hpp

创建共享内存的需要的用到的系统调,我们直接用这个头文件进行封装,然后prosessa 和 prosessb直接进行调用

获得key 

 

#include <iostream>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>using namespace std;
const string pathname = "/home/gx/linux-exercise";
const int proj_id = 0x666; // 这个你可以自己随便定一个。key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);if (key == -1){cerr << "ftok error" << endl;exit(1);}return key;}

创建共享内存 

 

int create_shm(int flag)
{key_t key = get_key();int n = shmget(key, SHM_SIZE, flag);if(n < 0){cerr << "shmget error" << endl;exit(1);        } return n;                    
}

这里我用a进程进行创建共享内存,b获得共享内存,这样的话对create_shm函数进行封装,a调用createShm这个函数。

int createShm()
{return create_shm(IPC_CREAT | IPC_EXCL | 0666);
}

b就调用getShm这个函数。 

int getShm()
{return create_shm(IPC_CREAT);
}

关于shmget的shmflg这个参数,前面文档已经有详细的说明。这里不在细说。

共享内存创建好了,就意味着我们能通信了吗?并没有,因为shmget是系统调用,是OS划分出来的一块区域。 是真实的物理内存,而进程用的是虚拟地址,我们需要用shmat进行挂接。通过挂接,挂接到进程地址空间。        

 挂接

 char* shmaddr = (char*)shmat(shmid,nullptr,0);

 取消挂接

 shmdt(shmaddr);

 删除共享内存

 shmctl(shmid, IPC_RMID, nullptr);

 

完整代码  

头文件

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>using namespace std;
const int SHM_SIZE = 4096; // 共享内存的大小 也就是4KB,我们申请的大小一般是4096的整数倍
const string pathname = "/home/gx/linux-exercise";
const int proj_id = 0x666; // 这个你可以自己随便定一个,只要保证唯一就行key_t get_key()
{key_t key = ftok(pathname.c_str(), proj_id);if (key == -1){cerr << "ftok error" << endl;exit(1);}return key;
}int create_shm(int flag)
{key_t key = get_key();int n = shmget(key, SHM_SIZE, flag);if (n < 0){cerr << "shmget error" << endl;exit(1);}return n;
}int createShm()
{return create_shm(IPC_CREAT | IPC_EXCL | 0666);
}
int getShm()
{return create_shm(IPC_CREAT);
}

进程a 

 

int main()
{int shmid = createShm();char *shmaddr = (char *)shmat(shmid, nullptr, 0);while (true){cout << "Enter a message: " ;fgets(shmaddr, 4096, stdin);}shmdt(shmaddr);shmctl(shmid, IPC_RMID, nullptr);return 0;
}

 进程b

 

int main()
{int shmid = getShm();char* shmaddr = (char*)shmat(shmid,nullptr,0);while(true){cout << "client say@:" << shmaddr << endl;sleep(1);}shmdt(shmaddr);return 0;   
}

代码运行起来,我们可以发现共享内存是不会同步的,一方不写,一方也不会阻塞。

有没有什么方法可以让它们同步互斥?用管道。 

 共享内存命令行操作

查看共享内存

指令:ipcs -m 

 

删除共享内存

指令:ipcrm -m shmid 

 

 最后说说 key 和shmid的区别 key是操作系统创建出来的,确定这个共享内存的唯一性。

而shmid是给用户用的。

 

 

 

 

 

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

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

相关文章

C语言栈的含义与栈数据操作代码详解!

引言&#xff1a;在本篇博客中&#xff0c;我们将学到数据结构——栈&#xff0c;讲到栈的含义与关于栈的数据操作代码。栈可以在顺序表、双向链表以及单链表的基础上实现&#xff0c;而于本篇博客中&#xff0c;我们选择在顺序表的基础上实现栈。 更多有关C语言和数据结构知识…

Ansible自动化运维工具 - playbook 剧本编写

一. inventory 主机清单 Inventory 支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内。 1.1 inventory 中的变量含义 Inventory 变量名 含义ansible_hostansible连接节点时的IP地址ansible_port连接对方…

CANdela/Diva系列1--CANdela Studio的基本介绍

大家好&#xff0c;这个系列主要给大家介绍跟诊断相关的Vector 工具CANdela和Diva&#xff0c;首先介绍CANdela。 目录 1.CANdela的简介&#xff1a; 2.如何打开CANdela 工程&#xff1a; 3.CANdela工程的详细介绍&#xff1a; 3.1 工具栏的介绍&#xff1a; 3.2 工作树的…

2024-05-07 商业分析-赚钱之前怎么预估风险-记录

摘要: 2024-05-07 商业分析-赚钱之前怎么预估风险-记录 赚钱之前怎么预估风险 好&#xff0c;大家好&#xff0c;今天我们来讲的是叫什么赚钱之前怎么评估风险啊&#xff0c;这个问题啊提的很好啊&#xff0c;我们待会儿来讲啊。呃&#xff0c;首先呢今天这个主题呢来自于昨天…

Python实现txt转Excel(坐标)

import pandas as pddef txt_to_excel(txt_file, excel_file):# 读取 txt 文件with open(txt_file, r) as f:lines f.readlines()# 将每行数据分割成多个单元格data []for line in lines:row line.strip().split( )data.append(row)# 将数据保存到 Excel 文件df pd.DataFra…

剧本杀小程序,为商家带来更多收益

剧本杀作为一种社交类游戏&#xff0c;关注度越来越高&#xff0c;目前&#xff0c;市场上剧本杀依然呈现上升发展趋势。 不过当下&#xff0c;在剧本杀市场中&#xff0c;大部分商家都开始使用小程序管理运营剧本杀。相对于线下剧本杀&#xff0c;线上剧本杀小程序便于商家管…

一起深度学习

CIFAR-10 卷积神经网络 下载数据集构建网络运行测试 下载数据集 batchsz 32cifar_train datasets.CIFAR10(data,trainTrue,transformtorchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),torchvision.transforms.ToTensor()]),downloadTrue)cifar_train …

基于FPGA的DDS波形发生器VHDL代码Quartus仿真

名称&#xff1a;基于FPGA的DDS波形发生器VHDL代码Quartus仿真&#xff08;文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; DDS波形发生器VHDL 1、可以输出正弦波、方波、三角波 2、可以控制输出波形的频率 DDS波形发生器原理…

什么是光伏发电?什么是分布式光伏系统?

一、光伏发电 光伏发电&#xff0c;作为一种可再生能源利用技术&#xff0c;其核心原理基于半导体的光生伏特效应。简而言之&#xff0c;光伏发电就是将太阳能直接转换为电能的过程。它由三个主要部分组成&#xff1a;太阳电池板&#xff08;组件&#xff09;、控制器和逆变器…

word:三线表的绘制【攻略】

word&#xff1a;三线表的绘制【攻略】 前言版权推荐word&#xff1a;三线表的绘制效果简单方法另外的方法 最后 前言 2024-5-7 18:25:08 以下内容源自《【攻略】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客…

经典面试题之滑动窗口专题

class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {// 长度最小的子数组 // 大于等于 targetint min_len INT32_MAX;// 总和int sum 0;int start 0; // 起点for(int i 0; i< nums.size(); i) {sum nums[i];while(sum > targe…

有了这么多套件,为什么还需要APaaS

文/明道云创始人任向晖 在明道云的业务活动中&#xff0c;比较常见的一个问题是和套件应用的关系。一个有具体应用需求的客户为什么不从市场上购买现成的套件应用&#xff0c;而要选择APaaS来构建呢&#xff1f;反过来说似乎也成立&#xff0c;既然一个平台什么应用都能搭建&a…

产品需求文档怎么写?超详细的产品需求文档PRD模板来了!

产品需求文档怎么写&#xff1f;如何写一份简洁明了、外行人看了就能秒懂的产品需求文档呢&#xff1f;今天这篇文章&#xff0c;就来和大家分享如何编写一份高质量的产品需求文档 PRD&#xff01; 下图是来自 boardmix 模板社区的「产品需求文档」模板&#xff0c;它给出了一…

【教学类-50-14】20240505“数一数”图片样式12:数一数(12个“人物”图案)

作品展示 背景需求&#xff1a; 前文做了“”材料”图片的数一数学具&#xff0c;效果不错&#xff0c; https://blog.csdn.net/reasonsummer/article/details/138466325https://blog.csdn.net/reasonsummer/article/details/138466325 为了让图案内容更丰富&#xff0c;我又…

MahApps.Metro的MVVM模式介绍(一)

MahApps.Metro是一个开源的WPF (Windows Presentation Foundation) UI 控件库。它的特点有现代化设计、主题定制、响应式布局、内置控件。 而Mvvm模式的核心思想是将用户界面&#xff08;View&#xff09;与应用程序逻辑&#xff08;ViewModel&#xff09;分离&#xff0c;以实…

2024.1.1 IntelliJ IDEA 使用记录

2024.1.1 IntelliJ IDEA 使用记录 下载设置文件编码maven 配置 插件可以中文语言包安装lombok 插件Smart Tomcat ( 根据需要安装)Smart Tomcat 配置 项目导入java 设置maven 配置 项目运行SpringBoot 项目运行tomcat 运行 (根据需要)相关依赖添加运行配置 下载 IntelliJ IDEA …

Buuctf-Misc题目练习

打开后是一个gif动图&#xff0c;可以使用stegsolve工具进行逐帧看。 File Format:文件格式 Data Extract:数据提取 Steregram Solve:立体试图 可以左右控制偏移 Frame Browser:帧浏览器 Image Combiner:拼图&#xff0c;图片拼接 所以可以知道我们要选这个Frame Browser …

Petalinux的使用——定制Linux系统

文章目录 配置petalinux运行环境petalinux设计流程 配置petalinux运行环境 Petalinux的安装在文章Ubuntu镜像源的更改及其Petalinux的安装中已经介绍&#xff0c;下面介绍petalinux运行环境的配置过程。 进入到petalinux的安装路径下&#xff0c;使用下面的命令对petalinux的运…

基于vue.js+thymeleaf模板引擎+ajax的注册登陆简洁模板(含从零到一详细介绍)

文章目录 前言1、数据库准备2、工具类与相关基类使用2.1、工具类2.2、相关基类 3、web包目录说明4、注册功能设计&#xff08;本文核心部分&#xff09;4.1、注册页面设计4.2、注册逻辑设计 5、登陆功能设计5.1、登陆页面设计5.2、登陆逻辑设计 6、运行效果图 前言 大多数的网…

.双链表.

题目&#xff1a; 实现一个双链表&#xff0c;双链表初始为空&#xff0c;支持 55 种操作&#xff1a; 在最左侧插入一个数&#xff1b;在最右侧插入一个数&#xff1b;将第 k&#x1d458; 个插入的数删除&#xff1b;在第 k&#x1d458; 个插入的数左侧插入一个数&#xf…