【Linux】详解如何利用共享内存实现进程间通信

一、共享内存(Shared Memory)的认识

        共享内存(Shared Memory)是多进程间共享的一部分物理内存。它允许多个进程访问同一块内存空间,从而在不同进程之间共享和传递数据。这种方式常常用于加速进程间的通信,因为数据不需要在不同的进程间进行拷贝。

        在操作系统中,共享内存通常是通过映射一段能被其他进程所访问的内存实现的。一个进程可以创建一个共享内存段,并将该段连接到其地址空间中。其他进程也可以将这段共享内存连接到它们的地址空间中。这样,所有进程都可以访问同一段内存,实现数据的共享。

        在内核中共享内存可以存在很多个,操作系统必须先创建描述共享内存的结构体,再把这些结构体组织起来管理。为了保证两个或者是多个进程看到同一个共享内存,就要给每一个共享内存提供唯一性的标识

二、创建共享内存的方法

        创建共享内存的方法为shmget,其中第一个参数为key,key就是共享内存在内核中的唯一标识。size是要设置的共享内存的大小(在内核中,共享内存是以4kb为基本单位的,我们在给共享内存分配大小的时候最好也是分配4kb的整数倍的大小。)。还有一个参数shmflg,shmflg可以有很多选项,但最常见的有两个:

  1. IPC_CREAT:如果共享内存不存在, 就创建之, 如果共享内存已经存在, 直接获取它。
  2. IPC_EXCL:不能单独使用, 没意义。
  3. IPC_CREAT | IPC_EXCL:如果共享内存不存在, 就创建之, 如果共享内存已经存在,就出错返回!!如果共享内存创建是成功的, 则一定是一个新的共享内存! 

        如果shmget成功获取或创建了共享内存段,它会返回一个非负整数,这个整数是共享内存段的标识符(也称为共享内存段的ID)。这个标识符在后续的共享内存操作中(如shmat和shmdt)会被使用。 

 2.1、key的获取

        这里的pathname是一串文件路径,proj_id是一个整数,这两个参数由用户随意指定,操作系统底层通过特定的算法帮我们形成一个key值,如果形成失败-1被返回。如果成功这个key值就会被设置进描述共享内存的结构体中用来标识这块共享内存的唯一性。通过给两个进程或者是多个进程传入同样的pathname和proj_id就能让它们看到同一块共享内存。 

三、查看共享内存的方法

采用ipcs指令可以查看系统中指定用户创建的共享内存,消息队列和信号量。

ipcs -m:查看系统中指定用户创建的共享内存

 ipcs -q:查看系统中指定用户创建的消息队列

  ipcs -s:查看系统中指定用户创建的信号量

四、指令删除共享内存的方法

ipcrm -m shmid(共享内存id):删除用户指定的共享内存。 

五、代码实现共享内存通信

5.1、获取key值

其实获取key可以封装成函数也可以不封装,这里我是将其封装成函数了。

key_t get_key(const char* pathname, int proj_id)
{key_t key = ftok(pathname, proj_id);//成功返回key值,失败返回-1if(key == -1){cout << "获取key值失败,原因是:" << strerror(errno) << endl;exit(1);}return key;
}

5.2、创建共享内存

        共享内存是为了实现两方或是多方通信的,这里我就设置成为两方通信。所以一定是一方创建共享内存,另一方获取共享内存。要注意的是,共享内存也是有权限的,所以创建的一方需要指明创建的共享内存的权限。

int get_or_create_shared_memory(key_t key, int size, int flag)
{int shmid = shmget(key, size, flag);//成功返回共享内存标识符,失败返回-1if(shmid == -1){cout << "共享内存创建失败,原因是:" << strerror(errno) << endl;exit(2);}return shmid;
}int create_shared_memory(key_t key, int size)
{return get_or_create_shared_memory(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int get_shared_memory(key_t key, int size)
{return get_or_create_shared_memory(key, size, IPC_CREAT);
}

  5.3、挂接共享内存/去挂接共享内存

        shmid表示要挂接的共享内存的shmid,shmaddr表示要将该共享内存挂接到进程地址空间的什么位置,其实这个我们不用管,操作系统会自行帮我们挂接,可以直接设置为nullptr,shmflg表示可以对该共享内存做什么操作,设置为0默认是可读可写。 如果挂接成功,返回挂接到进程地址空间的地址,如果挂接失败,返回-1。

5.4、同步操作

        如果读写共享内存的进程间没有进行同步操作,可能就会发生脏读,即写入的数据和读到的数据不一致。所以要进行进程同步操作。这里我借助了管道来进行同步操作,即写方写完了再唤醒读方来读。

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cstdio>using namespace std;#define MODE 0666 //权限
#define NAME "./fifo.txt"//定义命名管道结构体
class Fifo
{
private:string _name; // 文件路径加文件名
public:Fifo(const string &name): _name(name){int n = mkfifo(_name.c_str(), MODE);if (n == 0)cout << "创建管道成功!" << endl;elsecout << "创建管道失败!原因是:" << strerror(errno) << endl;};~Fifo(){int n = unlink(_name.c_str());if (n == 0)cout << "删除管道成功!" << endl;elsecout << "删除管道失败!原因是:" << strerror(errno) << endl;};
};//同步结构体
class Sync
{
private:int rfd;int wfd;
public:void open_read(){rfd = open(NAME, O_RDONLY);if (rfd == -1){cout << "读打开管道失败!" << endl;exit(1);}}void open_write(){wfd = open(NAME, O_WRONLY);if (wfd == -1){cout << "写打开管道失败!" << endl;exit(1);}}int wait(){int ret = 0;int n = read(rfd, &ret, sizeof(int));return n;}void wake_up(){int ret = 0;int n = write(wfd, &ret, sizeof(int));}
};

读写方分别创立一个sync对象,在读写的时候分别调用wait和wake_up方法进行同步。 

5.5、删除共享内存

         进程创建的共享内存如果在进程结束时没有释放,则共享内存会一直存在。也就是说,共享内存的声明周期是随内核的,如果我们没有主动去释放共享内存,除非重启系统,否则共享内存一直存在。所以在写端当你已经不写了时要将共享内存删掉。

 shmctl系统调用加上IPC_RMID选项可以删除共享内存。

void shm_del(int shmid)
{int ret = shmctl(shmid, IPC_RMID, nullptr);if (ret == -1)cerr << "删除共享内存失败" << endl;elsecout << "删除共享内存成功" << endl;
}

         shmctl的第三个选项可以传入一个描述共享内存的对象的地址来获取该共享内存的属性,如果只是删除共享内存,直接设置为nullptr即可。

六、总结 

         共享内存不提供进程间协同的任何机制。但是共享内存是所有进程间通信机制中速度最快的。因为共享内存是通过页表直接与进程地址空间中的地址产生关联的,写方只需要将数据拷贝到共享内存中,读方直接通过地址就能访问内容,无需进行数据的拷贝,直接就提高了访问数据的速度。也就是说共享内存进行进程间通信只需要一次数据的拷贝,而我们之前提到的管道通信,都是读方调用write函数将数据写入内存(进行了一次拷贝),读方再调用read函数将数据拷贝到用户层,要进行两次数据的拷贝。

七、说明

        因为实现共享内存的文件数较多,所以以上并不是全部代码,如果想获取全部实现代码,请移步到本人码云:C++代码: C++代码保存的地方 - Gitee.com

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

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

相关文章

Linux - 线程

目录 一.Linux线程的概念 1.1什么是线程 1.2 线程的优点 1.3 线程的缺点 1.4 线程异常 1.5 线程用途 二. Linux进程VS线程 2.1 进程和线程 三. Linux线程控制 3.1 POSIX线程库 3.2 创建线程 3.3 进程ID和线程ID 3.4 线程ID及进程地址空间布局 3.5 线程终止 3.6 线…

SASE:打造数据安全保障新模式

在企业纷纷拥抱数字业务的过程中&#xff0c;由于边缘计算、云服务、混合网络的逐渐兴起&#xff0c;使得本就漏洞百出的传统网络安全架构更加岌岌可危&#xff0c;而且远远无法满足企业数字业务的需要。 伴随企业全球化发展&#xff0c;企业的数据中心不再是用户与设备访问需…

全球7大指纹浏览器排行榜:哪个最适合你?

在数字时代&#xff0c;我们每一次上网都会留下独特的数字足迹&#xff0c;被称为“浏览器指纹”。为了保护这些私人信息不被滥用&#xff0c;指纹浏览器成为了一个重要工具。但是&#xff0c;并非所有的指纹浏览器都是一样的&#xff0c;它们各有特点&#xff0c;适用于不同的…

【春季发布】LinkSLA智能运维V6.0发布 聚焦架构升级 新增带外管理

LinkSLA智能运维为企业IT部门提供覆盖资源管理、监控告警、IT服务台、日志管理、MOC值守服务等多项功能为一体的运维平台&#xff0c;通过打通各业务单元、贯穿各技术栈&#xff0c;以故障定位和全生命周期管理为核心&#xff0c;持续保障业务连续性。 本次V6.0版本全面升级&a…

最佳AI实践|如何在 Dify 用 Workflow 构建一个 Blog SEO AI 应用?

最佳AI实践&#xff5c;如何在 Dify 用 Workflow 构建一个 Blog SEO AI 应用&#xff1f; 文章目录 最佳AI实践&#xff5c;如何在 Dify 用 Workflow 构建一个 Blog SEO AI 应用&#xff1f;常见的内容写作场景如何持续提升 AI 的写作能力&#xff1f;开始设计 Workflow确立 Wo…

Linux系统中LVM与磁盘配额

目录 一、LVM逻辑卷管理 二、LVM的管理命令 物理卷管理 卷组管理 逻辑卷管理 *创建并使用LVM步骤 三、磁盘配额概述 实现磁盘限额的条件 Linux 磁盘限额的特点 四、磁盘配额管理 磁盘限额 一、LVM逻辑卷管理 能够在保持现有数据不变的情况下动态调整磁盘容量&#…

git 分支-变基

在git中&#xff0c;将一个分支的更改集成到另一个分支有两种主要方式&#xff1a;合并&#xff08;merge&#xff09;和变基&#xff08;rebase&#xff09;。在本节中&#xff0c;将学习什么是变基&#xff0c;如何执行变基操作&#xff0c;为什么它是一个非常强大的工具&…

js脚本解决因挂VPN导致boss上高德地图无法正常规划公交路线问题

​ 情况说明&#xff1a; 最开始一直以为boss上的查询自己所在地点到面试地点的公交的功能有bug,总是规划路线失败&#xff0c;结果这个一调试&#xff0c;发现是自己的经纬度有问题导致的。 程序猿嘛&#xff0c;开机VPN必须是挂着的&#xff0c;这就导致了boss获取到了错误…

如何节约上架时间,小程序管理平台推荐

继微信正式推出微信小程序后&#xff0c;各个大厂陆续发布了各自的小程序平台 —— 支付宝小程序、百度小程序、头条小程序&#xff0c;各家不同的小程序标准一度让开发者们激情开骂&#xff0c;虽然目前跨平台的小程序开发可以通过taro、mpvue、kbone等跨平台开发框架来解决&a…

JookDB下载安装使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Android 纵向双选日历

这个日历的布局分两部分&#xff0c;一部分是显示星期几的LinearLayout&#xff0c;另外就是一个RecyclerView&#xff0c;负责纵向滚动了。 工具类&#xff1a; implementation com.blankj:utilcode:1.17.3上activity_calendar代码&#xff1a; <?xml version"1.0&…

Jmeter测试学习笔记

第一章 jmeter基础知识 一.Jmeter工具中的组件 1.测试计划&#xff1a;Jmeter测试的起点。容器。 2.线程组&#xff1a;代表一定的用户 3.取样器&#xff1a;发送请求的最小单元 4.逻辑控制器&#xff1a;处理请求逻辑 5.前置处理器&#xff1a;请求之前的操作 6.后置处…