Linux 进程间通信 System V系列: 共享内存,信号量,简单介绍消息队列

进程间通信 System V系列: 共享内存,初识信号量

  • 一.共享内存
    • 1.引入
    • 2.原理
    • 3.系统调用接口
      • 1.shmget
      • 2.shmat和shmdt
      • 3.shmctl
    • 4.边写代码边了解共享内存的特性
      • 1.ftok形成key,shmget创建与获取共享内存
      • 2.shm相关指令
      • 3.shmat和shmdt挂接和取消挂接
      • 4.shmctl获取共享内存信息,释放共享内存
      • 5.开始通信
    • 5.利用管道实现共享内存的协同机制
      • 1.Sync(同步类)
      • 2.读写端的修改
      • 3.动图演示
    • 6.共享内存的优缺点
  • 二.消息队列
    • 1.概念
    • 2.接口,数据结构等等
  • 三.信号量理论
    • 1.信号量的原理
    • 2.信号量的理论
      • 1.从生活中的例子理解信号量
      • 2.进程角度的信号量
      • 3.信号量的细节
        • 1.信号量必须要由OS提供并维护
        • 2.信号量的基本操作
    • 3.信号量的接口
      • 1.semget
      • 2.semctl
      • 3.semop
  • 四.System V系列的进程间通信的小总结
  • 五.利用信号量实现共享内存的协同机制
    • 1.思路
    • 2.Server创建并获取信号量,Client获取信号量 -> ftok和semget
      • 1.ftok
      • 2.shmget
    • 3.Server阻塞申请信号量资源 - semop
    • 4.Client初始化信号量资源 - semctl
    • 5.Server释放信号量资源 - semctl
    • 6.完整代码
      • 1.Common.hpp
      • 2.sem.hpp
      • 3.ShmServer.cpp
      • 4.ShmClient.cpp
    • 7.演示

我们不是都有管道了吗?为什么还要有其他的进程间通信方式呢?
当时的年代,通信技术是一个非常火的点,就像现在人工智能和各种大模型一样,类似于百家争鸣的样子,所以有很多进程间通信的方式

因为共享内存跟我们学的进程地址空间有密切联系,所以我们重点学习
而信号量我们就先认识一下,学习一下理论即可

一.共享内存

1.引入

管道方便是方便,直接复用文件接口即可,但是想要使用管道是需要访问内核的,而且管道的内核级缓冲区也是在内核当中的,因此会导致效率不是特别好(因为访问内核本身就是一个比较大的消耗)

那么有没有什么办法能够让两个进程无需访问内核就能进行进程间通信呢?
在这里插入图片描述

2.原理

跟命名管道一样,共享内存也是允许完全无关的两个进程商量一下一起使用同一份资源,从而实现进程间通信的
在这里插入图片描述
看似很好懂,但是有几个值得关注的点:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.系统调用接口

shm就是shared_memory:共享内存
ipc: InterProcess Communication:进程间通信

1.shmget

在这里插入图片描述
第三个参数为什么要这么设计呢?
我们一起来分析一下
在这里插入图片描述
我们刚才还没有说返回值
在这里插入图片描述

2.shmat和shmdt

分配完一个共享内存了,下面要做的事情就是把共享内存映射到进程的进程地址空间当中,并用页表建立该映射

shmat:shmattach是负责建立映射的,也就是将共享内存和进程挂接起来
shmdt:shmdetach(detach是分离,拆卸的意思),也就是取消该共享内存跟进程的挂接关系
在这里插入图片描述
shmdt直接传入shmat的返回值即可
在这里插入图片描述
shmat:如果挂接失败,返回(void*)-1

3.shmctl

在这里插入图片描述

4.边写代码边了解共享内存的特性

1.ftok形成key,shmget创建与获取共享内存

在这里插入图片描述
在这里插入图片描述
下面我们应该是要使用shmat和shmdt了,不过在此之前,我们还要介绍几个指令

2.shm相关指令

在这里插入图片描述
如何释放呢?
可以通过shmctl系统调用接口来释放,也可以通过指令来释放
我们先介绍指令释放
在这里插入图片描述
这里的key显示的是16进制,我们刚才打印的是10进制
因此我们改一下代码,让它以16进制打印
在这里插入图片描述

3.shmat和shmdt挂接和取消挂接

在这里插入图片描述
在这里插入图片描述

while :;do ipcs -m;sleep 1;done
这里反过滤掉了root创建的共享内存
while :;do ipcs -m | grep -v root;sleep 1;done

在这里插入图片描述
我们看到了挂接和取消挂接的全过程

4.shmctl获取共享内存信息,释放共享内存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们实现了共享内存的创建/获取,挂接,取消挂接和释放
下面是时候开始让这两个进程开始通信了
在挂接之后,取消挂接之前开始通信

5.开始通信

我们刚才获取信息只是为了告诉大家这个函数有这么个功能而已,因此我们就不调用这个获取信息的函数了哦
在这里插入图片描述
在这里插入图片描述
通信成功
在这里插入图片描述
那么没有协同机制怎么办?
一个很好的方法是借助信号量来解决这一问题,但是因为信号量的接口太麻烦(比共享内存的这些接口还要麻烦很多),因此我们以后详细介绍信号量的时候再去使用信号量的接口

要不然是像我刚才那样通信双方约定好一个暗号,读端读到暗号时意味着通信结束
而是那样只能解决一部分情况下保证读端读取完所有的写端数据时才退出
还是无法解决写端还没写入你读端就开始读了啊

我们可以利用天然具有协同机制的管道啊!!
又因为我们这两个进程是没有血缘关系的,因此我们用一下命名管道吧
这里直接把我们之前写的管理命名管道的代码拿过来

#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#include <cstring>
#include <string>
using namespace std;
const char* path="./namedpipe";
#define MODE 0666
class Fifo
{
public:Fifo(const char* path):_path(path)//用成员变量保存路径{int ret=mkfifo(_path.c_str(),MODE);if(ret==-1)//说明创建失败{cerr<<"create namedpipe fail, errno: "<<errno<<" , strerror: "<<strerror(errno)<<endl;}else{cout<<"create namedpipe succeed"<<endl;}}~Fifo(){unlink(_path.c_str());}
private:string _path;
};

5.利用管道实现共享内存的协同机制

1.Sync(同步类)

在这里插入图片描述
在这里插入图片描述

2.读写端的修改

在这里插入图片描述
在这里插入图片描述

3.动图演示

在这里插入图片描述
成功解决了写端没写数据,读端还读的问题

6.共享内存的优缺点

在这里插入图片描述
下面我们趁热打铁快速了解一下消息队列

二.消息队列

1.概念

在这里插入图片描述
消息队列的生命周期也是随内核的,跟共享内存一样

2.接口,数据结构等等

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三.信号量理论

1.信号量的原理

这里介绍信号量的原理,
一方面是为了让我们更好地理解信号量,一方面是先提出一些多线程当中的概念

在这里插入图片描述

2.信号量的理论

1.从生活中的例子理解信号量

在这里插入图片描述

2.进程角度的信号量

在这里插入图片描述

3.信号量的细节

1.信号量必须要由OS提供并维护

在这里插入图片描述

2.信号量的基本操作

在这里插入图片描述

3.信号量的接口

1.semget

在这里插入图片描述

2.semctl

在这里插入图片描述

3.semop

在这里插入图片描述
关于信号量的更多话题我们等到多线程的时候还会再说明的

四.System V系列的进程间通信的小总结

共享内存,消息队列和信号量的很多接口都是相同的,它们的内核数据结构当中也都有一个一样的结构体:ipc_perm,
它们都是主要应用于本地通信的,因此在目前的网络时代当中并不常用(用的更多的还是网络通信)

它们都属于System V系列的进程间通信,OS为了管理它们搞了一个
ipc_ids结构体,ipc_id_ary结构体,kern_ipc_perm结构体实现了ipc资源的动态申请和释放,并将对ipc资源的管理转换为了对kern_ipc_perm的增删查改和对ipc_id_ary的动态扩容
在这里插入图片描述
在这里插入图片描述
不过因为System V系列的进程间通信的结构和数据结构都是独立设计的,跟文件是完全解耦的,因此不符合Linux一切皆文件的设计思想,这也是System V系列的进程间通信并不热门的原因

如果OS能够在struct file当中封装一个ipc_perm的指针,把kern_ipc_perm关联起来,并利用文件接口封装ipc资源使用的接口,就能让System V系列的进程间通信符合一切皆文件

那样的话使用起来肯定也就更容易,肯定就能热门了

五.利用信号量实现共享内存的协同机制

本来想写完前4点就结束吧,不过心血来潮想用一下信号量,下面我们一起来用一下信号量吧

1.思路

依旧是回归我们之前需要利用管道实现共享内存的协同机制的时候
我们的目的是让读端一开始阻塞等待,等到写端准备要进行写入的时候告诉读端: 我要开始写啦,你也开始读吧

此时就能够保证读端不会在一开始的时候做无意义的读取操作

大致流程分为:

  1. 读端(Server)创建并获取信号量
  2. Server阻塞申请信号量资源,此时读端就是阻塞等待写端进行写入
  3. Client获取读端创建好的信号量
  4. 写端(Client)准备写入时初始化信号量资源
  5. Server成功申请信号量资源,开始进程间通信
  6. 最后Server释放信号量资源

流程很清晰,
(那为什么我们一开始不用信号量呢? 因为信号量接口太麻烦了…,而且我用管道和信号量来解决这一共享内存的同步机制是为了学习熟悉这些接口)

之前有一些点我没有注意到,写代码的时候屡屡碰壁,最后才搞过来了

2.Server创建并获取信号量,Client获取信号量 -> ftok和semget

1.ftok

在这里插入图片描述

  1. ftok里面传入的路径必须是我们Linux系统中的确存在的路径!!!
  2. 我们申请的共享内存和信号量各自的key是不可以相同的(大家也能够很好的理解,因为key才是ipc资源的唯一标识嘛)

我们就用这个产生sem的key
在这里插入图片描述
用这个产生shm的key
在这里插入图片描述

2.shmget

在这里插入图片描述
我们共享内存的资源就是一个整体,因此nsems传入1
然后跟共享内存一样,Server传IPC_CREAT | IPC_EXCL | 0666, Client传入IPC_CREAT即可
在这里插入图片描述
Server:
在这里插入图片描述
Client:
在这里插入图片描述

3.Server阻塞申请信号量资源 - semop

在这里插入图片描述
当sem_op<0即需要申请信号量时,如果信号量==0,那么该进程就会阻塞,等待信号量>0
而信号量在还没有被进程设置之前默认值是0,因此我们可以这样来玩
(注意:

  1. semget时传入的nsems时你申请的这个信号量集当中的信号量数目,而不是信号量的初始值!!
  2. 初始值需要进程显式传入,而且默认值是0[我就是因为这点屡屡碰壁]
  3. sembuf是本来就有的,不需要我们显式提供[我就是因为这点屡屡碰壁]
    )
    Server不设置信号量,在读取之前申请信号量资源阻塞等待写端进行写入(我们起名为lock函数)
    Client即将进行写入之前初始化该信号量为1(我们起名为Unlock函数),此时Server等待成功,退出阻塞状态,开始进行读取操作
    在这里插入图片描述

4.Client初始化信号量资源 - semctl

在这里插入图片描述
在这里插入图片描述

5.Server释放信号量资源 - semctl

在这里插入图片描述

6.完整代码

1.Common.hpp

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
using namespace std;const char* shm_path="/home/wzs/ubuntucode/process_ipc/semaphore";
const int shm_id=0x5678;
const int agreeSize=4096;key_t GetKey(const char* k_path,int proj_id)
{key_t ret=ftok(k_path,proj_id);if(ret==-1){cout<<"ftok fail"<<endl;exit(1);}return ret;
}class Shm
{
public:int GetShmid(int key,int size,int shmflg){int shmid=shmget(key,size,shmflg);if(shmid==-1){cout<<"shmget fail"<<endl;exit(1);}return shmid;}void* Attach(int shmid){void* addr=shmat(shmid,nullptr,0);if(addr==(void*)-1){cout<<"shmat fail"<<endl;exit(1);}return addr;}void Detach(void* addr){shmdt(addr);}void DelShm(int shmid){shmctl(shmid,IPC_RMID,nullptr);}
};

2.sem.hpp

#pragma once
#include <sys/sem.h>
const char* sem_path="/home/wzs/ubuntucode/process_ipc/pipe/namepipe/name_pipepool";
const int sem_id=0x3f45289;union semun {  int val;                /* 用于SETVAL */  struct semid_ds *buf;  /* 用于IPC_STAT和IPC_SET */  unsigned short *array; /* 用于GETALL和SETALL */  
};void ChangeCount(sembuf* buf,int val)
{buf->sem_num=0;buf->sem_op=val;buf->sem_flg=SEM_UNDO;
}class Sem
{
public:int GetSemid(key_t key,int nsems,int semflg){int semid=semget(key,nsems,semflg);if(semid==-1){cout<<"semget fail"<<endl;exit(1);}return semid;}void DelSem(int semid){semctl(semid,0,IPC_RMID);}void Change(int semid,sembuf* sops){cout<<"wait sem resource..."<<endl;semop(semid,sops,1);cout<<"wait success!"<<endl;}void GetInfo(int semid){int val=semctl(semid,0,GETVAL);cout<<val<<endl;}void Init(int semid,semun un){semctl(semid,0,SETVAL,un);}void Unlock(int semid){union semun sem_union;sem_union.val=1;//将信号量的初始值设置为1,此时相当于开锁,读端可以拿到信号量,开始读取Init(semid,sem_union);}void lock(int semid){sembuf buf;ChangeCount(&buf,-1);Change(semid,&buf);//我想申请信号量,但是信号量默认是0,我需要阻塞等待}
};

3.ShmServer.cpp

#include "Common.hpp"
#include "sem.hpp"
int main()
{Shm shm;key_t sem_key=GetKey(shm_path,shm_id);//获取Keyint shmid=shm.GetShmid(sem_key,agreeSize,IPC_CREAT | IPC_EXCL | 0666);//申请shmchar* addr=(char*)shm.Attach(shmid);//挂接shm//利用二元信号量(锁)Sem sem;key_t k=GetKey(sem_path,sem_id);int semid=sem.GetSemid(k,1,IPC_CREAT | IPC_EXCL | 0666);//等待获取锁sem.lock(semid);cout<<"receive message begin########################################"<<endl;//开始读取while(true){if(addr[0]=='q') break;cout<<"this is message:"<<addr<<"。"<<endl;sleep(1);}cout<<"receive message over#########################################"<<endl;cout<<"Server will detach shm now..."<<endl;shm.Detach(addr);//解除挂接shm.DelShm(shmid);//删除shmsem.DelSem(semid);//删除semreturn 0;
}

4.ShmClient.cpp

#include "Common.hpp"
#include "sem.hpp"
int main()
{Shm shm;key_t sem_key=GetKey(shm_path,shm_id);int shmid=shm.GetShmid(sem_key,agreeSize,IPC_CREAT);Sem sem;key_t k=GetKey(sem_path,sem_id);int semid=sem.GetSemid(k,1,IPC_CREAT);char* addr=(char*)shm.Attach(shmid);memset(addr,0,agreeSize);cout<<"send message begin########################################"<<endl;//开锁sem.Unlock(semid);for(char c='A';c<='Z';c++){addr[c-'A']=c;sleep(1);}addr[0]='q';cout<<"send message over########################################"<<endl;cout<<"Client will detach shm now..."<<endl;shm.Detach(addr);return 0;
}

7.演示

在这里插入图片描述

以上就是Linux 进程间通信 System V系列: 共享内存,信号量,简单介绍消息队列的全部内容,希望能对大家有所帮助!!

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

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

相关文章

政务网离线安装python3及其依赖手册

文章目录 python安装及环境配置gcc安装make安装python3安装pip安装 测试测试python3报错:ModuleNotFoundError: No module named _ctypes’测试pip3报错“pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.” 依赖库…

大模型prompt实例:知识库信息质量校验模块

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…

基于FPGA的数字信号处理(11)--定点数的舍入模式(2)向最临近值取整nearest

前言 在之前的文章介绍了定点数为什么需要舍入和几种常见的舍入模式。今天我们再来看看另外一种舍入模式&#xff1a;向最临近值取整nearest。 10进制数的nearest nearest&#xff1a; 向最临近值方向取整。它的舍入方式和四舍五入非常类似&#xff0c;都是舍入到最近的整数…

单链表经典oj题(2)

前言 这次将要把剩下的oj题将以图解和自己的理解把它讲解完&#xff0c;希望对大家有所帮助&#xff0c;这次的讲解也是干货 第一题 21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; ok这次就简单点&#xff0c;大家自己去看题目了 将两个升序链表合并为一个…

带有-i选项的sed命令在Linux上执行成功,但在MacOS上失败了

问题&#xff1a; 我已经成功地使用以下 sed 命令在Linux中搜索/替换文本&#xff1a; sed -i s/old_string/new_string/g /path/to/file然而&#xff0c;当我在Mac OS X上尝试时&#xff0c;我得到&#xff1a; command i expects \ followed by text我以为我的Mac运行的是…

未授权访问:Memcached 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章&#xff1a; 这里附上大…

K8S三 K8S部署微服务应用

一 用k8s部署微服务应用 以我们之前用docker部署过的eureka应用为例&#xff0c;首先添加配置文件eureka-app-deployment.yaml用于创建Deployment apiVersion: apps/v1 kind: Deployment metadata:name: eureka-app-deployment # deployment名字labels:app: eureka-app spec:…

【C++】CentOS环境搭建-升级CMAKE

【C】CentOS环境搭建-升级CMAKE CMAKE报错CMake 3.12 or higher is required. You are running version 2.8.12.2升级步骤1.移除当前的cmake2.安装必要的构建工具和库3.下载最新的cmake源码并解压5.编译和安装6.验证安装 CMAKE报错CMake 3.12 or higher is required. You are r…

MySQL存储引擎详解

存储引擎 MySQL体系结构 连接层&#xff1a;与客户端连接&#xff0c;权限校验、连接池服务层&#xff1a;SQL接口和解析、查询优化、缓存、函数引擎层&#xff1a;索引、存储引擎存储层&#xff1a;系统文件、日志&#xff08;Redo、Undo等&#xff09; 存储引擎介绍 不同的…

免费远程控制软件哪个好用

免费远程控制软件哪个好用 在现今高度信息化的社会&#xff0c;远程控制软件已成为许多用户进行远程办公、技术支持和教育培训的重要工具。市面上有许多免费的远程控制软件&#xff0c;但哪款才是最好用的呢&#xff1f;本文将为您介绍几款热门的免费远程控制软件&#xff0c;…

Matlab: ode45解微分方程——以弹簧振子模型为例

简介&#xff1a; 在科学和工程中&#xff0c;我们经常遇到描述事物变化的微分方程。这些方程可以帮助我们理解从行星运动到药物在体内的扩散等各种现象。但是&#xff0c;很多微分方程非常复杂&#xff0c;手动求解几乎不可能。这时&#xff0c;我们就可以使用像 ode45这样的…

【iOS】frame与bounds区别

文章目录 前言framebounds两者区别size的区别总结 前言 在学习响应者链的过程中用到了frame与bounds的混用&#xff0c;这两个属性经常出现在我们的开发中&#xff0c;特别撰写一篇博客分析区别 首先&#xff0c;我们来看一下iOS特有的坐标系&#xff0c;在iOS坐标系中以左上…

【debug】如何使用pycharm对代码调试

后续会将所有debug中遇到的知识放入&#xff0c;建议关注收藏 本站友情链接&#xff1a; 基本理论专栏&#xff08;当前更新好的debug所有内容都在这里&#xff09; 【debug】报错解决方法&#xff08;CondaHTTPError&#xff1a;HTTP 000 connection failed for url&#xff…

【回溯 状态压缩 深度优先】37. 解数独

本文涉及知识点 回溯 状态压缩 深度优先 LeetCode37. 解数独 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只…

[C++核心编程-06]----C++类和对象之对象模型和this指针

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

Microsoft 365 for Mac v16.84 office365全套办公软件

Microsoft 365 for Mac是一款功能丰富的办公软件套件&#xff0c;为Mac用户提供了丰富的功能和工具&#xff0c;提高了工作效率和协作能力。Microsoft 365 for Mac是一款专为Mac用户设计的订阅式办公软件套件&#xff0c;旨在提高生产力和效率。 Microsoft 365 for Mac v16.84正…

ubantu安装docker以及docker-compose

ubantu安装docker以及docker-compose 安装docker1、从官方存储库中安装Docker2、启动Docker服务3、验证 安装docker compose使用docker部署服务1、需要再opt文件夹下创建以下文件夹&#xff0c;/opt文件夹目录说明2、可将已备份对应文件夹拷至对应文件夹下3、在/opt/compose目录…

霍金《时间简史 A Brief History of Time》书后索引(A--D)

图源&#xff1a;Wikipedia INDEX A Abacus Absolute position Absolute time Absolute zero Acceleration Age of the universe Air resistance Albrecht, Andreas Alpha Centauri Alpher, Ralph Anthropic principle Antigravity Antiparticles Aristotle Arrows of time …

基于Vant UI的微信小程序开发(随时更新的写手)

基于Vant UI的微信小程序开发✨ &#xff08;一&#xff09;悬浮浮动1、效果图&#xff1a;只要无脑引用样式就可以了2、页面代码3、js代码4、样式代码 &#xff08;二&#xff09;底部跳转1、效果图&#xff1a;点击我要发布跳转到发布的页面2、js代码3、页面代码4、app.json代…

我觉得POC应该贴近实际

今天我看到一位老师给我一份测试数据。 这是三个国产数据库。算是分布式的。其中有两个和我比较熟悉&#xff0c;但是这个数据看上去并不好。看上去第一个黄色的数据库数据是这里最好的了。但是即使如此&#xff0c;我相信大部分做数据库的人都知道。MySQL和PostgreSQL平时拿出…