【Linux】进程间通信——命名管道和共享内存

目录

命名管道(named pipe)

命令行中使用

代码中使用

共享内存(shared memory)

shmget

ipcs命令

shmctl

shmat/shmdt

简单通信


命名管道(named pipe)

之前我们说了匿名管道,但是匿名管道有一个特点就是只能具有血缘关系的进程间可以进行通信,那如果我想让两个毫无关系的进程进行通信,该怎么办呢?

还是不要忘记进程间通信的本质,就是让两个进程看到同一份资源,如果两个进程毫无关系,那么我们可以让它们看到某个路径下的一个文件,这样它们就看到同一份资源了。并且,因为这类文件是唯一路径下的唯一文件,它是可以确定的,所以叫命名文件,创建的管道也就叫命名管道

不同的进程以不同的方式打开同一个文件,那么这两个进程的文件描述符表中的指针指向不同的文件结构体对象,但是不同的文件结构体对象指向的文件的缓冲区是一样的,所以,两个进程可以看到相同的内容。

命令行中使用

我们可以首先在命令行中用一下管道文件,先查一下手册

man mkfifo

这个命令的基本使用就是mkfifo + 文件名

我们也可以看到这个文件的类型是p,表示pipe,管道文件

创建完管道文件后就可以用两个进程 (命令就是进程),一个往管道文件中写,一个往管道文件中读(可以echo向文件中写,cat从文件中读)

现象就是一个往管道文件中写,如果没人读,那么进程就会阻塞等待;同理,一个往管道文件中读,如果没人写,那么进程也会阻塞等待

代码中使用

上面是在命令行上进行的操作,如果我们要用代码来操作就需要用相关的接口

man 3 mkfifo

第一个参数就是路径加文件名,第二个参数就是要创建的文件的权限

其实我们的任务就是创建一个管道文件,然后用文件操作(open、close、read、write)的方式向管道文件中写和读

我们可以创建一个服务器端和一个客户端,把客户输入的内容通过命名管道传给服务器,创建下面几个文件:

Comm.hpp中放共同的用得到的代码,PipeClient.cc放客户端的代码,PipeServer.cc放服务器端的代码,我们就来非常简单的模拟实现一下

基本代码如下,代码的效果就是先开服务器端,再开客户端,客户端写什么,服务器端都可以收到

//Comm.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<cerrno>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
using namespace std;#define PATH "./fifo"
#define Mode 0666
class Fifo
{public:Fifo(const char*path):_path(path){umask(0);int n=mkfifo(_path.c_str(),Mode);if(n==0){cout<<"touch fifo success"<<endl;}else{cout<<"fail to fifo errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;}}~Fifo(){int n=unlink(PATH);if(n==0){cout<<"fifo remove"<<endl;}else{cout<<"fail to remove fifo errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;}}private:string _path;
};
//PipeServe.cc
#include"Comm.hpp"int main()
{Fifo fifo(PATH);int rfd=open(PATH,O_RDONLY);//如果要是读先open,没人写,就要阻塞等待在这if(rfd<0){cout<<"fail to read open fifo errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;return 1;}cout<<"read open success"<<endl;char buffer[1024]={0};while(1){ssize_t n=read(rfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;cout<<"client say : "<<buffer<<endl;}else if(n==0){cout<<"client quit,me too"<<endl;break;}else {cout<<"fail to read errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;break;}}
close(rfd);return 0;
}
//PipeClient.cc
#include"Comm.hpp"int main()
{int wfd=open(PATH,O_WRONLY);if(wfd<0){cout<<"fail to write open fifo errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;return 1;}string inbuffer;while(1){cout<<"please input your message"<<endl;getline(cin,inbuffer);if(inbuffer=="quit")break;int n=write(wfd,inbuffer.c_str(),inbuffer.size());if(n<0){cout<<"fail to write errno is "<<errno<<" strerror is "<<strerror(errno)<<endl;break;}}close(wfd);return 0;
}

共享内存(shared memory)

之前说的无论是匿名管道还是命名管道都是管道,它们都是基于文件实现的,下面我们要说的是system V版本下的进程间通信的方式共享内存,消息队列,信号量

下面我们先说什么是共享内存

在谈论进程间通信时永远不要忘掉进程间通信的本质:让不同的进程看到同一份资源。

所以,共享内存就是在物理内存中开辟一段空间,然后将这块空间通过页表映射到各个进程的地址空间(虚拟内存)的共享区中,这样,不同的进程就可以看到同样的一份资源了。

有了上面的理论基础我们就知道了我们需要学习什么样的系统调用,有申请共享内存的,有把共享内存进行挂接到地址空间的,有去挂接的,也有删除共享内存的。当然这只是我们粗略的去描述,具体有啥我们下面来看:

shmget

首先要介绍的就是申请共享内存的系统调用

man shmget

它如果成功了就返回共享内存的一个标识符,这个跟下面的key不同,这个标识符是给人看的,是我们通过这个标识符唯一确定一个共享内存

它有三个参数,第一个是在内核中唯一标识一个共享内存的key值,就是内核通过这个值唯一确定一个共享内存,因为内核中存在多个共享内存这是正常的。具体说这个值是怎么创建的呢,当然我们可以自己给一个值,但是我们给的值容易过于简单,容易重复,所以就提供了一个接口

man ftok

这个函数有两个参数,其实我们可以随便给一个路径,一个proj_id,只要它们是唯一的即可,这样就可以生成唯一的key值,不同的进程间只要传入相同的路径和proj_id,它们就能获得相同的key值,找到同一块共享内存。

第二个参数就是你要创建的共享内存的大小,单位是字节

第三个参数是一些选项,你可以设置,基本的选项有:

IPC_CREAT:共享内存不存在就创建,如果存在就直接获取

IPC_EXCL:不能单独使用,没意义

IPC_CREAT | IPC_EXCL:共享内存不存在就创建,存在就出错返回

但是在这些选项后面还有按位或上权限,一般给0666

我们简单的写一个使用它们的代码

#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<cstring>
#include<cerrno>
using namespace std;
#define PATH "/home/user100"
#define proj_id 111
#define SIZE 4096
int main()
{key_t key=ftok(PATH,proj_id);if(key<0){cout<<"ftok fail errno is "<<errno<<" error string is "<<strerror(errno)<<endl;return 1;}int shmid=shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);if(shmid<0){cout<<"shmid fail errno is "<<errno<<" error string is "<<strerror(errno)<<endl;return 2;}cout<<"make shm success"<<endl;return 0;
}

然后就运行,发现,第一次运行成功,第二次运行失败

因为共享内存在程序结束后并不会删除,也就是说,共享内存的生命周期随内核,我们关掉xShell再开启也不会消失,因为云服务器是一直运行着的,除非我们重启系统共享内存才会消失

ipcs命令

我们可以通过命令和代码删除共享内存

图中的perm其实就是我们代码中设置的权限(permission)

nattch是这个共享内存挂接(attach)到了几个进程中

并且我们要说明的是内核中共享内存的大小是以4kb为基本单位的,也就是说如果我们给4097个字节,它也是会申请8kb的大小的,只不过会显示4097个字节

ipcs可以查看目前都有哪些消息队列,共享内存和信号量,shmid为1的共享内存就是我们刚才创建的,添加不同的选项可以分别查看

-q:显示消息队列的信息。(massage queue)

-m:显示共享内存的信息。(shared memory)

-s:显示信号量的信息。(semaphore array)

ipcrm -m shmid 就是删除共享内存,要是删消息队列和信号量也是以此类推

shmctl

下面就是用代码删除了,我们需要用到下面的接口

man shmctl

这个其实是对共享内存进行控制,当然了,控制也包括删除

第一个参数就是shmid,就是shmget的返回值

第二个参数是一些选项,不过我们删除要用到的选项是IPC_RMID

第三个参数是你传一个这个类型的结构体指针过去,然后它把共享内存的信息给你传回来,我们一般不需要信息的话给个nullptr就行了

我们也可以看一下内核都暴露给我们什么样的信息:

struct shmid_ds中第一个内容就是第二张图片的struct ipc_perm。

我们简单的用一下就行了

 int ret=shmctl(shmid,IPC_RMID,nullptr);if(ret<0){cout<<"remove shm fail errno is "<<errno<<" error string is "<<strerror(errno)<<endl;return 3;}cout<<"remove shm success,shmid is "<<shmid<<endl;

shmat/shmdt

上面我们讲了共享内存的创建和删除,下面我们就要说它该如何挂接和去挂接到进程的地址空间中呢?

man shmat(attach)挂接

man shmdt(detach)去关联

shmat第二个参数是一个地址,就是你想挂接到地址空间的哪里,但是我们一般给nullptr,就让OS随便找合适的空间即可

第三个仍然是一些选项,我们一般给0即可

返回值就是,挂接到的地址空间的首地址

shmdt参数就是shmat的返回值

我们简单来用一下

void *addr = shmat(shmid, nullptr, 0);if ((long long)addr == -1){cout << "shmat fail errno is " << errno << " error string is " << strerror(errno) << endl;}cout << "shmat success" << endl;sleep(5);int ret1 = shmdt(addr);if (ret1 < 0){cout << "shmdt fail errno is " << errno << " error string is " << strerror(errno) << endl;}cout << "shmdt success" << endl;sleep(2);

简单通信

上面是一些基础的使用,下面我们就像命名管道写代码完成两个进程间简单的通信,我们还是创建那么几个文件

我们不得不说,共享内存是最快的进程间通信的方式,因为一个进程只需要把数据写入到它的地址空间中(其实就是写到了物理内存中),另一个进程就可以看到,这是它的优点,但是共享内存是不提供进程间协同的机制的,就是你写你的我读我的,这就会导致你可能还没写完我就读了,这就导致信息丢失。但是我们知道管道是提供的,于是呢,我们可以利用管道的特点(写端不写,读端阻塞等待)实现共享内存的同步机制,基本代码如下:

//server.cc
#include "Comm.hpp"
#include "pipe.hpp"
int main()
{key_t key = Getkey();//获取keyint shmid = ServerGetshm(key);//创建共享内存void *addr = Attach(shmid);//挂接int *address = (int *)addr;Fifo fifo;//创建管道int rfd = Serveropenfifo();//以读方式打开文件for (int i = 0; i < 10; i++){wait(rfd);//写端不写,就阻塞在这里int *tmp = address;cout << "Server get ";while (*(tmp) != 0){cout << *(tmp);tmp++;}cout << endl;}Detach(addr);//去挂接removeshm(shmid);//删除共享内存return 0;
}
//client.cc
#include "Comm.hpp"
#include"pipe.hpp"
int main()
{key_t key = Getkey();//获取keyint shmid = ClientGetshm(key);//获取共享内存void *addr = Attach(shmid);//挂接int *address = (int *)addr;int wfd= clientopenfifo();//以写方式打开文件for (int i = 0; i < 10; i++){*(address + i) = i + 1;wakeup(wfd);//写完了一部分完整的数据就通知唤醒读端sleep(1);}Detach(addr);//去挂接return 0;
}
//comm.hpp
#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <cstring>
#include <unistd.h>
#include <cerrno>
using namespace std;
#define PATH "/home/user100"
#define proj_id 111
#define SIZE 4096key_t Getkey()
{key_t key = ftok(PATH, proj_id);if (key < 0){cout << "ftok fail errno is " << errno << " error string is " << strerror(errno) << endl;return -1;}return key;
}int ServerGetshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid < 0){cout << "Server Get shm fail errno is " << errno << " error string is " << strerror(errno) << endl;return -1;}cout << "make shm success" << endl;return shmid;
}
int ClientGetshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | 0666);if (shmid > 0)cout << "Client Get shm success" << endl;return shmid;
}void *Attach(int shmid)
{void *addr = shmat(shmid, nullptr, 0);if ((long long)addr == -1){cout << "shmat fail errno is " << errno << " error string is " << strerror(errno) << endl;return nullptr;}cout << "shmat success" << endl;return addr;
}int Detach(void *addr)
{int ret1 = shmdt(addr);if (ret1 < 0){cout << "shmdt fail errno is " << errno << " error string is " << strerror(errno) << endl;return -1;}cout << "shmdt success" << endl;return 0;
}int removeshm(int shmid)
{int ret = shmctl(shmid, IPC_RMID, nullptr);if (ret < 0){cout << "remove shm fail errno is " << errno << " error string is " << strerror(errno) << endl;return -1;}cout << "remove shm success,shmid is " << shmid << endl;return 0;
}
//pipe.hpp#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;#define Pipepath "./fifo"
#define Mode 0666
class Fifo
{
public:Fifo(const char *path = Pipepath): _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "touch fifo success" << endl;}else{cout << "fail to fifo errno is " << errno << " strerror is " << strerror(errno) << endl;}}~Fifo(){int n = unlink(Pipepath);if (n == 0){cout << "fifo remove" << endl;}else{cout << "fail to remove fifo errno is " << errno << " strerror is " << strerror(errno) << endl;}}private:string _path;
};int Serveropenfifo()
{int rfd = open(Pipepath, O_RDONLY);if (rfd < 0){cout << "fail to ropen fifo errno is " << errno << " strerror is " << strerror(errno) << endl;return 1;}return rfd;
}
int clientopenfifo()
{int wfd = open(Pipepath, O_WRONLY);if (wfd < 0){cout << "fail to wopen fifo errno is " << errno << " strerror is " << strerror(errno) << endl;return 1;}return wfd;
}void wakeup(int wfd)
{char ch='A';write(wfd,&ch,sizeof(ch));
}
void wait(int rfd)
{char buffer[10]={0};read(rfd,buffer,sizeof(buffer));
}

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

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

相关文章

为什么要使用加密软件?

一、保护数据安全&#xff1a;加密软件通过复杂的加密算法对敏感数据进行加密处理&#xff0c;使得未经授权的人员即使获取了加密数据&#xff0c;也无法轻易解密和获取其中的内容。这极大地提高了数据在存储、传输和使用过程中的安全性。 二、遵守法律法规&#xff1a;在许多国…

无人机图像目标检测

本仓库是人工智能课程的课程作业仓库&#xff0c;主要是完成无人机图像目标检测的任务&#xff0c;我们对visdrone数据集进行了处理&#xff0c;在yolo和ssd两种框架下进行了训练和测试&#xff0c;并编写demo用于实时的无人机图像目标检测。 requirements依赖&#xff1a; ss…

ubuntu虚拟机安装ssh时报错 正在等待缓存锁

问题&#xff1a; 连接vm ubuntu虚拟机安装ssh时报错 正在等待缓存锁。 sudo apt install openssh-server 处理办法 sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock

K8S ingress 初体验 - ingress-ngnix 的安装与使用

准备环境 先把 google 的vm 跑起来… gatemanMoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-user$ kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 124d v1.23.6 k8s-no…

python数据可视化(5)——绘制饼图

课程学习来源&#xff1a;b站up&#xff1a;【蚂蚁学python】 【课程链接&#xff1a;【【数据可视化】Python数据图表可视化入门到实战】】 【课程资料链接&#xff1a;【链接】】 Python绘制饼图分析北京天气 饼图&#xff0c;是一个划分为几个扇形的圆形统计图表&#xff…

爬虫-requests和Selenium

1、了解requests的功能 1.1 使用post和get发送请求 HTTP中常见发送网络请求的方式有两种&#xff0c;GET和POST。GET是从指定的资源请求数据&#xff0c;POST是向指定的资源提交要被处理的数据。 GET的用法&#xff1a; import requestsr requests.get("https://www.…

Milvus 核心设计(5)--- scalar indexwork mechanism

目录 背景 Scalar index 简介 属性过滤 扫描数据段 相似性搜索 返回结果 举例说明 1. 属性过滤 2. 扫描数据段 3. 相似性搜索 实际应用中的考虑 Scalar Index 方式 Auto indexing Inverted indexing 背景 继续Milvus的很细设计&#xff0c;前面主要阐述了Milvu…

Swift网络爬虫与数据可视化的结合 (1)

前言 在当今数字化时代&#xff0c;数据的重要性不言而喻。Swift&#xff0c;作为一种现代的编程语言&#xff0c;以其高性能、易用性和安全性&#xff0c;成为了开发iOS和macOS应用的首选。本文将探讨如何使用Swift来开发一个网络爬虫&#xff0c;以及如何将爬取的数据进行可…

图像边缘检测中Sobel算子的原理,并附OpenCV和Matlab的示例代码

Sobel算子是一种用于图像边缘检测的离散微分算子。它结合了图像的平滑处理和微分计算&#xff0c;旨在强调图像中强度变化显著的区域&#xff0c;即边缘。Sobel算子在图像处理中被广泛使用&#xff0c;特别是在计算机视觉和图像分析领域。 Sobel算子的原理 Sobel算子主要用于计…

【数学建模与优化】:解析与实践

目录 数学建模概述 1. 什么是数学模型 2. 数学模型的分类 2.1 按应用领域分类 2.2 按建模方法分类 2.3 按是否考虑随机因素分类 2.4 按变量的连续性分类 2.5 按对对象内部规律了解程度分类 2.6 按变量的基本关系分类 2.7 按是否考虑时间变化分类 3. 数学规划及优化模…

【车载开发系列】GIT教程---如何下载代码库

【车载开发系列】GIT教程—如何下载代码库 【车载开发系列】GIT教程---如何下载代码库 【车载开发系列】GIT教程---如何下载代码库一. 设置用户名和邮箱二. 生成SSH三. 登录远程github仓库配置四. Git中的ssh协议介绍五. 什么是GitLab六. GitLab与GitHub区别1&#xff09;用途和…

Python 获取今天(当天)、昨天(前一天)、前天(昨天的前一天)的开始时间、结束时间

描述&#xff1a;我这里是封装成DatetimeHelper工具类来调用 1.今天(当天)开始时间、结束时间 from datetime import datetime, timedeltaclass DatetimeHelper:# 获取今天(当天)的开始时间、结束时间(datetime类型)staticmethoddef getTodayStartEnd():# 获取当前的日期now …

nginx生成自签名SSL证书配置HTTPS

一、安装nginx nginx必须有"--with-http_ssl_module"模块 查看nginx安装的模块&#xff1a; rootecs-7398:/usr/local/nginx# cd /usr/local/nginx/ rootecs-7398:/usr/local/nginx# ./sbin/nginx -V nginx version: nginx/1.20.2 built by gcc 9.4.0 (Ubuntu 9.4.0…

HarmonyOS 屏幕适配设计

1. armonyOS 屏幕适配设计 1.1. 像素单位 &#xff08;1&#xff09;px (Pixels)   px代表屏幕上的像素点&#xff0c;是手机屏幕分辨率的单位&#xff0c;即屏幕物理像素单位。 &#xff08;2&#xff09;vp (Viewport Percentage)   vp是视口百分比单位&#xff0c;基于…

如何在excel表中实现单元格满足条件时整行变色?

可以试试使用条件格式&#xff1a; 一、条件格式 所谓“自动变色”就要使用条件格式。 先简单模拟数据如下&#xff0c; 按 B列数字为偶数 为条件&#xff0c;整行标记为蓝色背景色。 可以这样设置&#xff1a; 先选中1:10行数据&#xff0c;在这里要确定一下名称栏里显示…

元器件基础学习笔记——磁珠

一、磁珠的作用及构造 1.1 磁珠的作用 磁珠是一种用于抑制高频噪声的被动电子组件&#xff0c;通常由铁氧体材料制成&#xff0c;这种材料具有高电阻率和高磁导率&#xff0c;使其能够在高频下有效地将干扰信号以热能的形式消耗掉。在电路设计中&#xff0c;磁珠被广泛用于信号…

SQL Server设置定时作业调度Schedule

文章目录 SQL Server代理 SQL Server代理 SQL Server提供了一种称为SQL Server代理的功能&#xff0c;可以用来设置定时作业。以下是在SQL Server中设置定时作业的步骤&#xff1a; 打开SQL Server Management Studio&#xff08;SSMS&#xff09;。 连接到要设置定时作业的数…

Kotlin标准函数(语法糖)let with run also apply快速讲解

目录 1、知识储备——扩展函数 原理 定义扩展函数 调用扩展函数 2、返回值为上下文对象的标准函数 apply also 3、返回值为Lambda表达式结果 let run with 4、一表总结 1、知识储备——扩展函数 原理 Kotlin 在不继承父类或实现接口下&#xff0c;也能扩展一个类的…

N-(4-Azido-2-nitrophenyl)-N‘‘-biotinylnorspemidine

​一、基本信息 常用名&#xff1a;N-(4-Azido-2-nitrophenyl)-N-biotinylnorspemidine 英文名&#xff1a;N-(4-Azido-2-nitrophenyl)-N-biotinylnorspemidine CAS号&#xff1a;786609-83-4 分子式&#xff1a;C22H33N9O4S 分子量&#xff1a;519.62 二、结构特点 该化…

数据(图像)增广

一、数据增强 1、增加一个已有数据集&#xff0c;使得有更多的多样性&#xff0c;比如加入不同的背景噪音、改变图片的颜色和形状。 2、增强数据是在线生成的 3、增强类型&#xff1a; &#xff08;1&#xff09;翻转 &#xff08;2&#xff09;切割 &#xff08;3&#xf…