前言:        

        上文我们讲到了匿名管道【Linux系统】匿名管道以及进程池的简单实现-CSDN博客

        本文我们来讲一讲命名管道与共享内存


命名管道

        上面我们讲到,匿名管道只能用于有血缘关系(尤其父子)的进程进行通信!但如果我们想让没有关系的进程进行通信,该怎么办呢?命名管道就是答案!

        进程间通信的本质是让不同的进程看到同一份资源!命名管道也是一样!

1.命名管道原理

        1.命名管道与匿名管道一样,本质上都是文件!

        2.命名管道不同与匿名管道,命名管道是有名字、有路径的!

        3.如下图,创建命名管道只会返回一个fd。并且当多个进程打开同一个文件时,系统并不会将其加载多次。

        4.命名管道同匿名管道一样,其缓冲区不会刷新到磁盘中!

        5.如何保证多个进程打开的是同一个命名管道?路径!路径是唯一的!

2.命名管道的特性

命名管道的特性与匿名管道基本一样!唯一区别就是:命名管道可用于不相关进程间的通信!

5种特性:

命名管道,可用于不相关的进程通信
命名管道文件,自带同步机制:包含5种通信情况!
命名管道的面向字节流的
命名管道是单向通信的!(属于半双工的特殊情况。半双工:任何时候一个发,一个收。全双工:任何时候,可以同时收发)
命名管道的生命周期是由管道文件是否被删除决定的!

5种通信情况:

只要有一方没有打开管道文件,另一方就会阻塞在open处!直到都打开了管道文件,才会向下继续执行!
写慢,读快:读端阻塞,等待写端
写快,读慢:管道缓冲区写满了,就要阻塞等待读端
写关闭,读继续:一直读取,知道读到完,返回0,表示读取到文件末尾
写继续,读关闭:无意义操作!OS会自动杀掉写端进程(通过信号:13 SIGPIPE杀掉)

3.命名管道的接口

指令方面
//创建命名管道
mkfifo  管道名//删除命名管道
rm  管道名
unlink  管道名
yc@hyc-alicloud:~$ mkfifo t1hyc@hyc-alicloud:~$ ls -l
total 8
prw-rw-r--  1 hyc hyc    0 Aug 22 12:01 t1hyc@hyc-alicloud:~$ unlink t1
hyc@hyc-alicloud:~$ ls -l
total 8

        我们可以看到,创建的管道文件第一个字母为:p!这代表管道文件!

代码方面

创建命名管道:

#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);pathname:FIFO 文件路径(如 /tmp/myfifo),进程通过该路径访问管道。
mode:文件权限(如 0666 表示读写权限,需结合进程的 umask 计算实际权限)。
返回值:成功返回 0,失败返回-1

打开命名管道:

#include <fcntl.h>
int open(const char *pathname, int flags);flags:打开模式,需指定 O_RDONLY(只读,读端)或 O_WRONLY(只写,写端)
返回值:成功返回fd,失败返回-1

删除命名管道:

#include <unistd.h>
int unlink(const char *pathname);FIFO文件被删除后,已打开的进程仍可继续使用对应资源,直到所有进程关闭文件描述符后,资源才彻底释放

4.利用命名管道实现通信

        值得一提的,命名管道的同步机制是:只要有一方没有打开管道文件,另一方就会阻塞在open处!直到都打开了管道文件,才会向下继续执行!

//comm.hpp#pragma once
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
using namespace std;// 目标:实现client与service的通信#define EXIT(m)             \do                      \{                       \perror(m);          \exit(EXIT_FAILURE); \} while (0)class NameFifo
{
public:NameFifo(string path, string name): _path(path), _name(name){_fd = -1;_PATH = _path + "/" + _name;}// 创建管道void Create(){int n = mkfifo(_PATH.c_str(), 0666);if (n < 0){EXIT("mkfifo");}cout << "命名管道创建成功!\n";}// 打开管道void OpenForRead(){_fd = open(_PATH.c_str(), O_RDONLY);if (_fd < 0){EXIT("open");}cout << "读端打开成功!\n";}void OpenForWrite(){_fd = open(_PATH.c_str(), O_WRONLY);if (_fd < 0){EXIT("open");}cout << "写端打开成功!\n";}// 读取数据void Read(){char buffer[1024];int n = read(_fd, buffer, sizeof(buffer) - 1);buffer[n] = 0;printf("接收到数据:%s\n", buffer);}// 写数据void Write(){string msg;cout << "请输入内容:\n";cin >> msg;write(_fd, msg.c_str(), msg.size());}// 关闭管道void Close(){close(_fd);unlink(_PATH.c_str());cout << "管道:" << _fd << "关闭并删除!\n";}private:string _path;string _name;string _PATH;int _fd;
};//service.cc#include "comm.hpp"int main()
{NameFifo nf(".", "myfifo");nf.Create();nf.OpenForWrite();nf.Write();nf.Close();
}//client.cc#include "comm.hpp"int main()
{//service已经创建了管道,这里不用再创建了!NameFifo nf(".", "myfifo");nf.OpenForRead();nf.Read();nf.Close();
}


system V共享内存

system V共享内存也是进程间通信的一重要方式!

1.system V

        system V是Linux系统中的一种标准。它规定了系统调用接口的设计,共享内存正是满足了这一标准。

2.共享内存的原理

        如图,顾名思义共享内存就是将相同的内存空间,通过页表映射到不同的进程中去,达到不同进程访问同一个数据的效果(既IPC)

        1.想要完成上面的操作系统通过操作系统提供的系统调用来实现!

        2.取消内存与进程之间的映射关系,OS会自动的释放共享内存

        3.一个操作系统必然存在多个共享内存供给多个进程使用,所以OS一定会去管理共享内存,至于如何管理,我们后面说!

3.共享内存接口

创建or获取共享内存:

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);key:共享内存的唯一标识(通过 ftok 函数生成)
size:共享内存段的大小(字节),创建新段时必须指定,获取已有段时可设为 0shmflg:标志位(获取)IPC_CREAT:若不存在则创建新段,若存在则打开这个共享内存,并返回(创建)IPC_EXCL:与 IPC_CREAT 配合使用(单独使用没有意义),若指定要创建的共享内存已经存在则返回错误,否则创建(想要给出权限:如0666)成功:返回共享内存段标识符(shmid,非负整数);
失败:返回 -1,并设置 errno
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);pathname:指向一个已存在的文件路径的字符串,ftok 会使用该文件的 inode 编号 和 设备编号 作为生成键值proj_id:一个 8 位的非 0 整数用于区分同一文件对应的不同 IPC 对象,范围为1~255成功:返回一个 key_t 类型的键值(非负整数)
失败:返回 -1,并设置 errno 表示错误原因

让物理内存地址与虚拟地址进行映射:

void *shmat(int shmid, const void *shmaddr, int shmflg);shmid:shmget返回的共享内存 ID
shmaddr:指定映射到进程地址空间的起始地址。通常设为NULL,由内核自动分配
shmflg:映射选项,如SHM_RDONLY(只读映射,默认是读写)返回值:成功返回映射后的内存起始地址(void*),失败返回(void*)-1(设置errno)

解除映射关系:

int shmdt(const void *shmaddr);参数:shmaddr为shmat返回的共享内存起始地址
返回值:成功返回0,失败返回-1(设置errno)

删除or查询状态:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);shmid:共享内存 IDcmd:控制命令,常用:
IPC_RMID:标记共享内存段为待删除(所有进程分离后实际删除)
IPC_STAT:获取共享内存属性,存储到buf指向的struct shmid_ds结构中
IPC_SET:修改共享内存属性(需进程有足够权限)
buf:指向struct shmid_ds的指针(用于IPC_STAT/IPC_SET),IPC_RMID时可设为NULL返回值:成功返回0,失败返回-1(设置errno)

4.共享内存特性

在上面的接口中,我们会发现共享内存中存在两个标示符:唯一标识符key、内存段标识符shmid

理解:

  key 是用户层 “告诉内核要找哪个内存段” 的标识,shmid 是内核 “告诉用户层如何操作这个内存段” 的句柄。两者的交互是 “用户层用keyshmid,再用shmid操作内存段”。简而言之,查找时用key,操作时用shmid!!!

如何保证不同的进程访问的是同一个内存呢?那当然是key了!只要对ftok传入相同的函数,就可以得到相同的key,从而找到相同的内存段!
共享内存的生命周期是随内核的!如果不显示的删除,那么就算进程退出了,共享内存仍然存在!
共享内存大小:必须是4KB(4096)的整数!

同步机制:

不同于管道,共享内存本身是没有同步机制的!

共享内存属于用户空间,用户可以直接访问!

那其优点就是:速度快,映射之后可以直接看到资源,并可以直接读取!没有限制的

但缺点就是,没有同步机制,这会导致数据不被保护!

比如:写数据写到一半,就被读取走了!(管道调用系统调用,会被内核保护起来。而共享内存是没有内核保护的)

5.利用共享内存实现进程间通信

//Shm.hpp#pragma once
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string>
#include <iostream>
using namespace std;// 目标:利用共享内存,实现service和client的通信#define SIZE 4096
#define gmode 0666
#define EXIT(m)             \{                       \perror(m);          \exit(EXIT_FAILURE); \}class Shm
{
public:Shm(string &pathname, int &projid){_key = ftok(pathname.c_str(), projid);}// 创建共享内存void Creat(){umask(0);_shmid = shmget(_key, SIZE, IPC_CREAT | IPC_EXCL | gmode);if (_shmid < 0){EXIT("shmget");}cout << "创建共享内存成功!\n";}// 获取共享内存void Get(){_shmid = shmget(_key, SIZE, IPC_CREAT);if (_shmid < 0){EXIT("shmget");}cout << "获取共享内存成功!\n";}// 映射共享内存至虚拟空间void Attach(){_start_mem = shmat(_shmid, NULL, 0);if ((long long)_start_mem < 0){EXIT("shmat");}cout << "映射成功!\n";}void Destroy(){UnAttach();int n = shmctl(_shmid, IPC_RMID, NULL);if (n < 0){EXIT("shmctl");}cout << "删除共享内存成功!\n";}// 获取开始地址void *Start(){return _start_mem;}private:// 解除映射void UnAttach(){int n = shmdt(_start_mem);if (n < 0){EXIT("shmdt");}cout << "解除映射成功!\n";}key_t _key;int _shmid;void *_start_mem;
};//service.cc#include "Shm.hpp"
#include <unistd.h>int main()
{string pathname = ".";int projid = 0x66;Shm shm(pathname, projid);shm.Creat();shm.Attach();// 写入数据char *arr = (char *)shm.Start();for (int i = 'a'; i <= 'z'; i++){arr[i - 'a'] = i;sleep(1);}shm.Destroy();
}//client.cc#include "Shm.hpp"
#include <unistd.h>int main()
{string pathname = ".";int projid = 0x66;Shm shm(pathname, projid);shm.Get();shm.Attach();// 读取数据while (1){printf("%s\n", (char *)shm.Start());sleep(1);}shm.Destroy();
}

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

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

相关文章

搜索体验优化:ABP vNext 的查询改写(Query Rewrite)与同义词治理

&#x1f50e; 搜索体验优化&#xff1a;ABP vNext 的查询改写&#xff08;Query Rewrite&#xff09;与同义词治理 &#x1f4da; 目录&#x1f50e; 搜索体验优化&#xff1a;ABP vNext 的查询改写&#xff08;Query Rewrite&#xff09;与同义词治理1. 背景与问题界定 &…

Text2API与Text2SQL深度对比:自然语言驱动的数据交互革命

在数字化浪潮中&#xff0c;如何让人机交互更加自然流畅&#xff1f;Text2API与Text2SQL技术应运而生&#xff0c;它们如同魔法般将自然语言转化为机器可执行的指令&#xff0c;让数据交互不再高不可攀。本文将深入剖析这两项技术的原理、优劣势及应用场景&#xff0c;带您领略…

数据可视化与分析平台设计与实现案例

数据可视化与分析平台设计与实现案例(python) 下面分享一个完整的 Flask 数据可视化与分析平台代码,包含所有必要的组件和功能。这个平台允许用户上传数据文件、进行基本的数据清洗、生成各种可视化图表以及查看基础统计分析结果。 产品设计 核心功能 数据上传与管理(支…

Kotlin-基础语法练习二

接上一篇博客 每个 Kotlin 程序都是由两种部分组成的&#xff1a; 1、表达式&#xff08;Expressions&#xff09;&#xff1a;用于计算值的部分&#xff0c;比如 2 3、函数调用、变量赋值等&#xff0c;它们通常会返回一个结果。2、语句&#xff08;Statements&#xff09;…

与Deepseek对话了解单片机基础知识

keil5里的c语言编程的程序烧录到单片机里具体过程是啥&#xff1f;如何能把机器语言转换为电路控制&#xff1f; 步骤 所在位置 核心工具 输入->输出 比喻 1. 编译 Keil5 (PC) 编译…

利用背景图片定位套打档案封面

某些表单设计起来比较复杂&#xff0c;或只有表单的空白图片资料。Nhdeep档案目录套打工具&#xff08;nhdeep官网www.nhdeep.com&#xff09;支持将已有的表单图片作为模版背景图片&#xff0c;然后使用文本框进行精准的位置定位&#xff0c;再进行文本替换。 背景图片定位套…

微信HOOK 实现自动下载视频

1、前言 在收发消息的接口中&#xff0c;图片和文件这类接口是相对容易自动下载&#xff0c;但是视频的下载是需要手动点击的&#xff0c;并且只有这一种下载方式&#xff0c;实现自动化也比较困难&#xff0c;一些项目的开发中&#xff0c;需要自动下载收到的视频并保存&#…

【GPT入门】第57课 详解 LLamaFactory 与 XTuner 实现大模型多卡分布式训练的方案与实践

【GPT入门】第57课 大模型多卡计算1. 理论2.LLamaFacotory实践3. xtuner3.1 介绍3.1 安装3.2 xtuner训练3.4 训练后格式转换3.5 合并基础模型与lora模型3.6 参数说明3.7 训练过程主观检验1. 理论 deepspeed的三种训练方式 zero-1&#xff0c;优化器状态分片。的优势体现在多卡…

部队多媒体信息发布系统:赋能 IPTV 与电教化,加速军营信息化变革

在科技飞速发展的当下&#xff0c;部队的信息化建设也在不断推进。多媒体信息发布系统作为一种创新的技术手段&#xff0c;正逐步融入部队的各个领域&#xff0c;为部队的现代化建设注入强大动力。​在部队 IPTV 方面&#xff0c;多媒体信息发布系统展现出卓越的性能。它打破了…

FTP/TCP上传下载文件

封装C风格地ftplib为ftp.c和ftp.h文件&#xff1a;cftplient类&#xff08;主要成员变量&#xff1a;文件大小、文件修改时间、主要成员函数&#xff1a;get函数&#xff08;远程文件名、本地文件名、核对文件时间&#xff09;、put函数&#xff08;本地文件名、服务端文件名、…

DeepSeek V3.1深度解析:一个模型两种思维,迈向Agent时代的第一步!

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录一、什么是DeepSeek V3.1&#xff1f;为什么这么火&#x1f680;1. 发布时间线回顾2.…

VsCode 便携版(绿色版)下载及配置

下载 VsCode 便携版&#xff0c;并确保所有配置和扩展都保存在一起&#xff0c;实现真正的“绿色版”效果 核心步骤概览 核心原理是在 VSCode 的主程序目录下创建一个名为 data 的文件夹&#xff0c;VSCode 启动时如果检测到这个文件夹&#xff0c;就会自动切换到便携模式&am…

使用VLLM部署大模型embedding/chat 的API

模型下载&#xff1a;一般通过modelscope提供的方式进行下载&#xff0c;速度更快&#xff0c;huggingface下模型即便开启了魔法也还是很慢&#xff0c;对于9B以上的模型都是至少15G的。 比如需要下载qwen3-embedding-8b的模型&#xff0c;可以通过提供的一段代码自动进行下载到…

Blender模型动画导入到UE5

UE5支持直接导入FBX文件&#xff0c;但在实际应用中笔者发现&#xff1a;刚开始使用的是UE5.3&#xff0c;在UE5.3中直接将.fbx文件拖入UE中导入后是一个个的零件&#xff0c;后来使用了datasmith插件等其他办法&#xff0c;怎么都没有达到想要的效果。后面升级UE5.4以后&#…

Promise详解:Promise解决ajax回调嵌套问题

目录 一、Promise是什么 二、回调地狱 三、Promise解决回调地狱的原理 四、promaise实例 一、Promise是什么 1、主要用于异步计算 2、可以将异步操作队列化&#xff0c;按照期望的顺序执行&#xff0c;返回符合预期的结果 4、可以在对象之间传递和操作promise&#xff0c…

【Kubernetes知识点】Pod调度和ConfigMaps

目录 1.如何将特定Pod调度到指定的节点&#xff1f; 2.什么是节点的亲和性&#xff1f; 3.什么是污点&#xff0c;它的主要用途是什么&#xff1f; 4.解释ConfigMap的作用。 5.Secret和ConfigMap相比较有哪些优点。 6.解释ResourceQuota的作用 1.如何将特定Pod调度到指定…

火车头使用Post方法采集Ajax页面教程

前面有写过一篇瀑布流的采集方法&#xff0c;今天在添加一个POST方法来采集Ajax刷新页面的教程。 之前的文章请看&#xff1a;火车头采集动态加载Ajax数据&#xff08;无分页瀑布流网站&#xff09; 如果遇到POST方法来架子Ajax数据&#xff0c;这和我之前写的是两个类型&…

【学习记录】structuredClone,URLSearchParams,groupBy

structuredClone() 可以进行深拷贝&#xff0c;这里有详细讲解&#xff1a;Window&#xff1a;structuredClone() 方法 当需要处理包含嵌套对象或数组的复杂数据结构时&#xff0c;建议使用 structuredClone() 来保护原始数据。 举例&#xff1a;别再用 … 扩展运算符了&#x…

30条AI编程指令

大家好&#xff0c;小机又来分享AI了。 前言&#xff1a; 凌晨三点&#xff0c;你还在像素级对齐那个永远对不齐的按钮&#xff1b;刚写完的API文档&#xff0c;产品经理一句"需求变了" 让你瞬间崩溃&#xff1b;更扎心的是&#xff0c;实习生用AI十分钟搞定了你要…