预备知识:

进程通信

进程需要某种协同,协同的前提条件是通信。有些数据是用来通知就绪的,有些是单纯的传输数据,还有一些是控制相关信息。

进程具有独立性,所以通信的成本可能稍微高一点;进程间通信前提是让不同进程看到同一份(操作系统)资源(“一段内存”)。

一定是某一个进程先需要通信,让OS创建一个共享资源,OS必须提供很多系统调用,OS创建的共享资源不同,系统调用不同,进程通信就会有不同种类。

System V

System V是一套本地通信的标准,通信方式有:

1.消息队列

2.共享内存

3.信息量

直接复用内核代码直接通信:命名管道和匿名管道。

管道

匿名管道

父子进程的文件表类似于浅拷贝,不会创建第二份struct file;进程会默认打开标准输入输出,就是拷贝了bash的files_struct,继承了它的文件描述符;struct file内部也包含了引用计数(内存级),因此子进程fd关闭不影响父进程文件使用。

在这里,父子进程看到同一份资源(内核级缓冲区),这个资源叫管道文件,父进程从缓冲区读,子进程缓冲区写,就能进行通信,当然,这里的管道通信是单向的。

让父子进程关掉不需要的描述符,缓冲区不需要刷新,因为不需要写到磁盘文件,而且写入效率也低。

创建管道文件

如果想要双向通信,可以创建两个管道,单项通信更简单一些,如果在一个缓冲区双方都要读写,那么会更复杂。

代码:

#include<iostream>
#include<unistd.h>
#include<cstring>
using namespace std;
void wrprc(int fd)
{string s = "syx 666";int ct = 0;int id = getpid();while (1){string message = to_string(id) + ":" + s + to_string(ct++);write(fd, message.c_str(), message.size());cout << message << endl;sleep(1);}
}
void rdprc(int fd)
{int id = getpid();char buffer[512];while (1){ssize_t n = read(fd, buffer, 511);if(n>0){buffer[511] = '\0';cout << id << ' ' << n << ":" << buffer << endl;sleep(1);}}
}
int main()
{int pipefd[2];int n = pipe(pipefd);if(n!=0){cerr << "error:" << errno << "errorstring" << strerror(errno) << endl;return 1;}//0 read 1 writecout << pipefd[0] << ':' << pipefd[1] << endl;pid_t id = fork();if(id==0){close(pipefd[0]);wrprc(pipefd[1]);close(pipefd[1]);}else{close(pipefd[1]);rdprc(pipefd[0]);close(pipefd[0]);}return 0;
}

运行:

管道的5种特征:

1.匿名管道只能用来进行具有血缘关系的进程之间通信,如父子进程。

2.管道内部,自带进程之间同步的机制(多执行流执行代码的时候,具有明显的顺序性),父进程读取节奏与子进程写的节奏保持一致,缓冲区可能存在被多个进程同时访问的情况,导致数据不一致问题(小于PPIPE_BUF字节,写入是原子的,安全的)。

3.管道文件的生命周期是跟随进程的生命周期。

4.管道文件在通信的时候,是面向字节流的,写入和读取的次数不是一一对应的(读取会读取一大堆)。

5.管道的通信模式,是一种特殊的半双工模式。(不能同时两个都写)

管道的4种情况

1.如果管道内部是空的并且写的fd没有关闭,读取条件不具备,读进程会被阻塞。

2.如果管道被写满,并且fd不读且没被关闭,写进程会被阻塞。

3.管道一直再被读,但是写被关闭了,读端会一直读到0,表示读到文件结尾。

4.读fd被关闭,OS会杀掉对应写的进程(发送13号信号)。

进程池

提前创建一批子进程,有任务将任务交给子进程执行,当管道没数据,子进程就在阻塞等待,父进程想哪个管道写入,就是唤醒哪个子进程来处理任务,当然,父进程要进行后端任务的负载均衡(都忙起来)。

//task.hpp
#pragma 
#include<iostream>
using namespace std;
#include<stdlib.h>
#include<unistd.h>
void Download()
{cout << "download!" << endl;
}
void Print()
{cout << "print!" << endl;
}
void Flush()
{cout << "flush!" << endl;
}
typedef void (*task)();
#define NUM 3
task tasks[NUM];
void loadTask()
{srand(time(nullptr) ^ getpid());tasks[0] = Download;tasks[1] = Print;tasks[2] = Flush;
}
void exec(int number)
{if(number<0||number>2){return;}tasks[number]();
}
int selectTask()
{return rand() % NUM;
}
//processpool.c
#include<iostream>
#include<string>
#include<unistd.h>
using namespace std;
#include<vector>
#include"task.hpp"
#include<sys/wait.h>
class Channel
{
public:Channel(int w,pid_t i){wfd = w;id = i;}pid_t getid(){return id;}int getwfd(){return wfd;}void closewfd(){close(wfd);}private:int wfd;pid_t id;
};
void work(int rfd)
{while(1){int command = 0;int n = read(rfd, &command, sizeof(command));if (n == sizeof(int)){exec(command);}else{close(rfd);break;}}
}
void createChannel(int num,vector<Channel>* v)
{for (int i = 0; i < num; i++){int pipefd[2] = {0};int n = pipe(pipefd);if (n < 0)exit(2);pid_t id = fork();if(id==0){for(auto i:*v){i.closewfd();}close(pipefd[1]);work(pipefd[0]);exit(0);}        close(pipefd[0]);v->emplace_back(Channel(pipefd[1], id));}}
void sendTask(Channel ch,int task)
{write(ch.getwfd(),&task,sizeof(task));
}
int main(int argc,char* argv[])
{if(argc!=2){cerr << "number error!" << endl;return 1;}vector<Channel> channels;int now = 0;loadTask();int num = std::stoi(argv[1]);//1.创建子进程和通信createChannel(num, &channels);//2.通过channel发放任务int cnt = 4;while (cnt--){int task = selectTask();cout << channels[now].getid() << ' ' << task << ':' << endl;sendTask(channels[now], task);now++;now %= num;sleep(1);}// 3.回收管道和子进程for(auto &i:channels){i.closewfd();int status;int pid=0;pid = waitpid(i.getid(), &status, 0);if(pid<0)cerr << "error!" << endl;}return 0;
}

这里要注意,子进程创建会继承父进程的文件描述符表,一定要把不需要写端关掉。

命名管道

如果两个进程毫无关系,那么就用命名管道进行通信。

创建命名管道:

mkfifo filename
int mkfifo(const char* filename,mode_t mode);

删除目录下文件:

int unlink(const char* path);

server和client进程通信(server读,client写):

#namePipe.hpp
#pragma 
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<cerrno>
#include<fcntl.h>
using namespace std;
#define Size 256
const string Path = "./myfifo";
#define creater 1
#define user 2
class NamedPipe
{
public:NamedPipe(const string &Path,int id) :path(Path),id(id){if(id==creater){int ret = mkfifo(path.c_str(), 0666);if(ret!=0){perror("mkfifo");}}openNamedPipe();}int removeNamedpipe(){int ret = unlink(path.c_str());if(ret!=0){perror("mkfifo");}return ret;}~NamedPipe(){if(id==creater)removeNamedpipe();}int Read(string* out){char buffer[Size];int n = read(fd, buffer, Size);if(n>0){buffer[Size] = 0;*out = buffer;}return n;}void Write(const string& in){write(fd, in.c_str(), in.size());}private : const string path;int id;int fd;int openNamedPipe(){if(id==creater)fd = open(path.c_str(), O_RDONLY);elsefd = open(path.c_str(), O_WRONLY);return fd;}
};
#server.cpp
#include"namedPipe.hpp"
int main()
{NamedPipe pipe(Path,creater);while(1){string messages;int n=pipe.Read(&messages);if(n==0){cout << "client quit!" << endl;break;}cout << messages << endl;}return 0;
}
#client.cpp
#include"namedPipe.hpp"
int main()
{NamedPipe pipe(Path,user);while(1){cout << "in:";string messages;cin >> messages;pipe.Write(messages);}return 0;
}

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

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

相关文章

基于Spring Boot的快递物流仓库管理系统 商品库存管理系统

&#x1f525;作者&#xff1a;it毕设实战小研&#x1f525; &#x1f496;简介&#xff1a;java、微信小程序、安卓&#xff1b;定制开发&#xff0c;远程调试 代码讲解&#xff0c;文档指导&#xff0c;ppt制作&#x1f496; 精彩专栏推荐订阅&#xff1a;在下方专栏&#x1…

脚手架开发-Common封装基础通用工具类<基础工具类>

书接上文 java一个脚手架搭建_redission java脚手架-CSDN博客 以微服务为基础搭建一套脚手架开始前的介绍-CSDN博客 脚手架开发-准备配置-进行数据初始化-配置文件的准备-CSDN博客 脚手架开发-准备配置-配置文件的准备项目的一些中间件-CSDN博客 脚手架开发-Nacos集成-CSD…

软件系统运维常见问题

系统部署常见问题 环境配置、兼容性问题。生产与测试环境的操作系统、库版本、中间件版本不一致&#xff0c;运行环境软件版本不匹配。新旧版本代码/依赖不兼容。依赖缺失或冲突问题。后端包启动失败&#xff0c;提示类/方法/第三方依赖库找不到或者版本冲突。配置错误。系统启…

2021 IEEE【论文精读】用GAN让音频隐写术骗过AI检测器 - 对抗深度学习的音频信息隐藏

使用GAN生成音频隐写术的隐写载体 本文为个人阅读GAN音频隐写论文&#xff0c;部分内容注解&#xff0c;由于原文篇幅较长这里就不再一一粘贴&#xff0c;仅对原文部分内容做注解&#xff0c;仅供参考详情参考原文链接 原文链接&#xff1a;https://ieeexplore.ieee.org/abstra…

PWA技术》》渐进式Web应用 Push API 和 WebSocket 、webworker 、serviceworker

PWA # 可离线 # 高性能 # 无需安装 # 原生体验Manifest {"name": "天气助手", // 应用全名"short_name": "天气", // 短名称&#xff08;主屏幕显示&#xff09;"start_url": "/index.html&…

数据结构——栈和队列oj练习

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 这一题需要我们充分理解队列和栈的特点。 队列&#xff1a;队头出数据&#xff0c;队尾入数据。 栈&#xff1a;栈顶出数据和入数据。 我们可以用两个队列实现栈&#xff0c;在这过程中&#xff0c;我们总要保持其…

Java基础 8.19

目录 1.局部内部类的使用 总结 1.局部内部类的使用 说明&#xff1a;局部内部类是定义在外部类的局部位置&#xff0c;比如方法中&#xff0c;并且有类名可以直接访问外部类的所有成员&#xff0c;包含私有的不能添加访问修饰符&#xff0c;因为它的地位就是一个局部变量。局…

从父类到子类:C++ 继承的奇妙旅程(2)

前言&#xff1a;各位代码航海家&#xff0c;欢迎回到C继承宇宙&#xff01;上回我们解锁了继承的「基础装备包」&#xff0c;成功驯服了public、protected和花式成员隐藏术。但——⚠️前方高能预警&#xff1a; 继承世界的暗流涌动远不止于此&#xff01;今天我们将勇闯三大神…

【图像算法 - 16】庖丁解牛:基于YOLO12与OpenCV的车辆部件级实例分割实战(附完整代码)

庖丁解牛&#xff1a;基于YOLO12与OpenCV的车辆部件级实例分割实战&#xff08;附完整代码&#xff09; 摘要&#xff1a; 告别“只见整车不见细节”&#xff01;本文将带您深入实战&#xff0c;利用YOLO12-seg训练实例分割模型&#xff0c;结合OpenCV的强大图像处理能力&…

ubuntu22.04配置远程桌面

文章目录前言检查桌面类型xorg远程桌面(xrdp)安装xrdpxrdp添加到ssl-certwayland远程桌面(gnome-remote-desktop)检查安装开启开启状况检查自动登录奇技淫巧前言 在windows上使用远程桌面服务&#xff0c;连接ubuntu主机的远程桌面 检查桌面类型 查看桌面类型、协议 echo $…

SQL Server 中子查询、临时表与 CTE 的选择与对比

在 SQL Server 的实际开发过程中&#xff0c;我们常常需要将复杂的查询逻辑分解为多个阶段进行处理。实现这一目标的常见手段有 子查询 (Subquery)、临时表 (Temporary Table) 和 CTE (Common Table Expression)。这三者在语法、执行效率以及可维护性方面各有优势与局限。如何选…

肖臻《区块链技术与应用》第20-22讲 - 以太坊难度调整、权益证明和智能合约

以太坊的“冰河时代”:详解难度调整算法与“难度炸弹” 摘要: 为了实现远快于比特币的十几秒出块速度,以太坊必须设计一套更为灵敏和复杂的挖矿难度调整算法。本文基于北京大学肖臻老师的公开课内容,深入剖析了以太坊独特的逐块难度调整机制。文章首先解释了其维持15秒平均…

C++中内存池(Memory Pool)详解和完整示例

1. 什么是内存池&#xff1f; 内存池&#xff08;Memory Pool / Pool Allocator&#xff09; 是一种内存管理机制&#xff0c;提前向系统申请一大块内存&#xff0c;再在这块内存里切分、分配和回收。 它相当于在用户空间建立了一层 “小型堆管理器”&#xff0c;避免频繁调用系…

测试 Next.js 应用:工具与策略

1. 引言 Next.js 作为一个基于 React 的全栈框架&#xff0c;在构建复杂 Web 应用时&#xff0c;测试是确保代码质量、功能稳定性和用户体验的关键步骤。测试可以分为单元测试、集成测试和端到端测试三种类型&#xff0c;每种类型针对不同的层面&#xff1a;单元测试验证单个组…

IP 分片和组装的具体过程

IP 分片和组装的具体过程 在这里插入图片描述 • 16 位标识(id): 唯一的标识主机发送的报文. 如果 IP 报文在数据链路层被分片了, 那么每一个片里面的这个 id 都是相同的. • 3 位标志字段: 第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到). 第二位置为 1 表示…

数据仓库OLTPOLAP维度讲解

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;大数据、Java、测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/…

OpenHarmony之编译配置白名单机制深度解析:构建系统的安全防线

一、白名单机制概述 在OpenHarmony的构建系统中&#xff0c;compile_standard_whitelist.json是一个关键的安全验证机制&#xff0c;它作为编译过程中的"守门人"&#xff0c;确保只有经过验证的组件和依赖关系才能被纳入最终构建产物。这个机制是OpenHarmony构建系统…

backward怎么计算的是torch.tensor(2.0, requires_grad=True)变量的梯度

import torch import torch.nn as nn import torch.optim as optim# 一个参数 w 2 w torch.tensor(2.0, requires_gradTrue) # 预测值 y_pred w * 3 # 6 # 真实值 y_true torch.tensor(10.0) # 损失 (预测 - 真实)^2 loss (y_pred - y_true) ** 2 # (6-10)^2 16loss.b…

戴永红×数图:重构零售空间价值,让陈列创造效益!

风雨同舟&#xff0c;智赢未来。近日&#xff0c;湖南戴永红商业连锁有限公司&#xff08;以下简称“戴永红”&#xff09;正式携手数图信息科技有限公司&#xff0c;全面启动“可视化品类空间管理”项目。以数图可视化陈列系统为引擎&#xff0c;双方将共同推进企业零售管理的…

排查Redis数据倾斜引发的性能瓶颈

以下是针对 Redis 数据倾斜问题的完整排查与优化方案&#xff0c;结合实战案例说明如何提升吞吐量和响应速度&#xff1a;一、问题现象定位1. ​性能监控异常​# Redis集群节点负载差异 $ redis-cli -c cluster nodes | grep master e1d7b... 10.0.0.1:637916379 master - 0 16…