Linux 进程间通信之管道
个人主页:仍有未知等待探索-CSDN博客
专题分栏: Linux
目录
一、通信
1、进程为什么要通信?
1.数据的类型
2.父进程和子进程算通信吗?
2、进程如何通信?
3、进程通信的常见方式?
二、管道
1、概念
1.管道的4种情况:
2.管道的5种特征:
2、匿名管道
1.为什么父子进程会向同一个显示器终端打印数据?
2.进程默认会打开三个标准输入输出:0,1,2 ?
3.为什么我们子进程主动close(0/1/2),不影响父进程继续使用显示器文件呢?
4.什么是管道文件?
5.管道文件的特点?
6.管道文件结构图
7.父子既然要关闭不需要的fd,那为什么要打开?
8.可以不关不需要的fd吗?
9.如果想双向通信,怎么办?
10.为什么要单向通信?
11.管道的使用(*)
12.read、write的注意点:
read的返回值:
write:
13.命令行中的管道符号:‘ | ’
14.管道多次创建的示意图(*)
3、命名管道
1.原理
2.匿名管道和命名管道的区别
3.怎么保证两个毫不相干的进程,打开了同一个文件呢?
4.命名管道操作 --- 系统调用篇
5.命名管道操作 --- 指令篇
三、管道的项目 --- 进程池
process_pool.cc
task.hpp
Makefile
一、通信
1、进程为什么要通信?
进程也是需要某种协同的,所以如何协同的前提是通信。
1.数据的类型
通知就绪的、单纯的要传递的数据、控制相关的信息...
2.父进程和子进程算通信吗?
答案:不算。
1、一直通信 和 能通信不是同一个概念。
2、父子进程只能读父子进程共享的变量内容,而且还不能进行修改,修改了就会发生写时拷贝,数据不一致。
2、进程如何通信?
进程间通信的前提是:
让两个进程能同时看到同一份资源(也就是操作系统的一段空间)。
因为不同的进程之间互相是独立的,所以不同的进程独自创建的空间,其他进程是不知道的。所以需要第三方的协助(也就是操作系统)。
但是,操作系统不能被用户直接的访问资源,所以操作系统需要提供对应的系统调用。
如果对于下列的图不明白的话,建议去看看进程相关的知识。
这两篇博客讲的都是和进程相关的内容。
Linux 冯诺依曼体系、操作系统、进程概念、进程状态、进程切换-CSDN博客
Linux 进程优先级、程序地址空间、进程控制-CSDN博客
3、进程通信的常见方式?
二、管道
1、概念
1.管道的4种情况:
1、如果管道内部时空的 && write fd没有关闭,读取条件不具备,读进程会被阻塞 --- wait --- 等到读取条件具备 --- 再进行写入 --- 之后再进行读数据。管道空且写未关,读阻塞
2、如果管道被写满 && 不读且没有关闭,写条件不具备,写进程会被阻塞 --- wait --- 等到写条件具备 --- 写入数据。管道满且读未关,写阻塞
3、管道一直在读&&写端关闭了wfd,读端read返回值会读到0,表示读到文件结尾。读且写关闭,读到文件尾,结束
4、rfd直接关闭,写端wfd一直进行写入。这种管道叫broken pipe,os不做浪费时空的事情,os会杀掉对应的进程,给目标发送(13 SIGPIPE)信号。写且读关闭,写进程被杀掉。
2.管道的5种特征:
1、匿名管道:只能用来进行具有父子进程之间通信。(这里的父子进程是泛泛的,爷孙进程也可以)。
2、管道内部,自带进程之间的同步机制。同步:多执行流执行代码的时候,具有明显的顺序性。
3、管道的生命周期是随进程的。
4、管道在通信的时候,是面向字节流的。 write的次数和读取的次数不是一一匹配的。
5、管道的通信模式,是一种特殊的半双工模式。(半双工:在同一时刻,只能有一个方向的数据传输)
2、匿名管道
1.为什么父子进程会向同一个显示器终端打印数据?
因为父子进程代码和数据在没有修改的时候是属于共享的,所以子进程创建的时候,会直接将父进程的task_struct拷贝一份,当然,也包括其中的文件描述符表,所以只要父进程的文件描述符表中存储了标准输入、标准输出、标准错误,子进程也就会有标准输入、标准输出、标准错误。
bash进程可以想象成树形结构的根节点。所以只要将bash的文件描述符表设置好,所有的进程就都会在运行的之后默认打开这三个标准文件。
2.进程默认会打开三个标准输入输出:0,1,2 ?
bash打开了,所有子进程默认也就打开了,我们只需要做好约定即可!
3.为什么我们子进程主动close(0/1/2),不影响父进程继续使用显示器文件呢?
因为,struct file中也有内存级引用计数。
父子同时打开了显示器文件,所以显示器文件的引用计数为2,当关闭了子进程的显示器文件,引用计数减一,只有当引用计数为0的时候,才会释放资源。
struct file -> ref_count; if (ref_count==0) 释放文件资源。
4.什么是管道文件?
管道文件:管道文件是一种特殊的文件,它存在于内存中,而不是磁盘上。它允许一个进程的输出直接作为另一个进程的输入,从而实现进程间的数据交换和协同工作。
5.管道文件的特点?
管道只允许单向通信。
管道文件不需要刷新到磁盘中(所以需要单独设计通信接口)。
6.管道文件结构图
7.父子既然要关闭不需要的fd,那为什么要打开?
为了让子进程继承下去。
8.可以不关不需要的fd吗?
可以,但是建议关闭,因为可能会误写。
9.如果想双向通信,怎么办?
可以建立两个管道。
一个管道,父读子写;一个管道父写子读。
10.为什么要单向通信?
因为简单,并且保证数据的安全性。
公共资源可能会存在被多个进程同时访问的情况。数据不一致问题。比如说子进程写了一半,父进程就开始读。
11.管道的使用(*)
#include <iostream> #include <string> #include <cerrno> // errno.h #include <cstring> // string.h #include <unistd.h> #include <cstdlib> #include <sys/types.h> #include <sys/wait.h>void subprocess_write(int); void fatherprocess_read(int);// 子写父读 int main() {// 创建管道int pipefd[2] = {0};int exitcode = pipe(pipefd);std::cout << "pipefd[0]: " << pipefd[0] << " pipefd[1]: " << pipefd[1] << std::endl;// 创建子进程int id = fork();// 子写父读if (id == 0){std::cout << "子进程关闭不需要的fd, 准备发消息" << std::endl;sleep(1);// child// 关闭其他权限close(pipefd[0]);// 子进程写subprocess_write(pipefd[1]);close(pipefd[1]);exit(0);}std::cout << "父进程进程关闭不需要的fd, 准备收消息" << std::endl;sleep(1);// father// 关闭不需要的fdclose(pipefd[1]);// 父进程读fatherprocess_read(pipefd[0]);close(pipefd[0]);pid_t rid = waitpid(id, nullptr, 0);if (rid > 0){std::cout << "wait sucessfully\n" << std::endl;}return 0; }std::string other() {static int cnt = 0;std::string messageid = std::to_string(cnt);cnt++;std::string stringid = std::to_string(getpid());std::string message = messageid + " my pid is " + stringid;return message; } void subprocess_write(int wfd) {std::string message = "i am child: ";while (true){std::string info = message + other();write(wfd, info.c_str(), info.size());// 写入管道的时候没有写入'\0'sleep(1);} } void fatherprocess_read(int rfd) {char inbuffer[1024] = {0};while (true){std::cout << "---------------------------------" << std::endl;ssize_t n = read(rfd, inbuffer, sizeof(inbuffer) - 1);if(n > 0){inbuffer[n] = 0;std::cout << inbuffer << std::endl;}else if (n == 0){std::cout << "读端关闭" << std::endl;break;}else {std::cout << "读入失败" << std::endl;break;}} }
12.read、write的注意点:
read的返回值:
- 返回值 > 0,读取成功,且返回值是读取的字节数。
- 返回值 = 0,读取结束。写端关闭,导致读端读到文件尾结束。
- 返回值 < 0,读取失败。
write:
如果每次写的数据的大小 小于 pipe_buf(<= 512Byte,在Linux中是4096Byte),写入操作的过程是原子的。(原子的意思就是不可再分,证明这个操作是线程安全的)
13.命令行中的管道符号:‘ | ’
命令行中的 |,就是匿名管道,‘|’ 左侧命令的stdout 作为 右侧命令的stdin。
14.管道多次创建的示意图(*)
创建了两个管道:
创建了三个管道:
3、命名管道
1.原理
是不是感觉很眼熟,和匿名管道的文件结构图差不多,其实就是差不多。
只不过这两个管道有一个非常显著的不同。
2.匿名管道和命名管道的区别
- 匿名管道:两个通信的进程之间是有血缘关系的。(父子进程,爷孙进程...)
- 命名管道:两个通信的进程之间没有血缘关系。
- 匿名管道:不需要文件路径。
- 命名管道:需要文件路径。
3.怎么保证两个毫不相干的进程,打开了同一个文件呢?
通过文件的路径来确认。每一个文件,都有文件路径(唯一性)。
4.命名管道操作 --- 系统调用篇
5.命名管道操作 --- 指令篇
三、管道的项目 --- 进程池
有 n 个管道,n 个任务,要保证每条管道都负载均衡。
process_pool.cc
#include "Task.hpp"// 父进程写,子进程读
class Channel
{
private:int _wfd;int _id;public:Channel(int wfd, int id): _wfd(wfd), _id(id){}void close_subprocess(){close(_wfd);}int get_wfd(){return _wfd;}int get_id(){return _id;}
};void work(int rfd);
void create_ChannelAndSubprocess(std::vector<Channel> *channels, int child_number, work_t work);
int next_channel(std::vector<Channel> &channels);
void send_command(Channel &channel, int option_task);
void ctrl_channel(std::vector<Channel> &channels);
void clean_ChannelAndSubprocess(std::vector<Channel> *channels);
int main(int argv, char* argc[])
{std::vector<Channel> channels;load_task();if (argv != 2)return -1;int num = std::stoi(argc[1]);// 1、创建信道和子进程create_ChannelAndSubprocess(&channels, num, work);// 2、通过信道控制子进程ctrl_channel(channels);// 3、回收管道和子进程clean_ChannelAndSubprocess(&channels);return 0;
}void create_ChannelAndSubprocess(std::vector<Channel> *channels, int child_number, work_t work)
{for (int i = 0; i < child_number; i++){int pipefd[2] = {0};int exit_code = pipe(pipefd);if (exit_code < 0){std::cout << "errno : " << exit_code << std::endl;exit(1);}pid_t id = fork();if (id == 0){// child;close(pipefd[1]);work(pipefd[0]);exit(0);}// fatherclose(pipefd[0]);(*channels).emplace_back(pipefd[1], id);}std::cout << "创建了:" << channels->size() << "个信道和子进程" << std::endl;
}
int next_channel(std::vector<Channel> &channels)
{static int count = 0;int option = count;count++;count %= channels.size();return option;
}
void send_command(Channel &channel, int option_task)
{write(channel.get_wfd(), &option_task, sizeof(option_task));
}
void ctrl_channel(std::vector<Channel> &channels)
{// 选择任务srand(time(nullptr));int n = task_number;while (n -- ){int option_task = rand() % task.size(); // task[option_task]// 选择信道int option_channel = next_channel(channels); // channels[option_task]// 发送任务send_command(channels.at(option_channel), option_task);sleep(1);}
}
void clean_ChannelAndSubprocess(std::vector<Channel> *channels)
{for (auto& channel:*channels){channel.close_subprocess();}for (auto& channel:*channels){waitpid(channel.get_id(), 0, 0);}
}
task.hpp
#pragma once#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>typedef void (*task_t)();
typedef void (*work_t)(int);const int size = 4096;
const int task_number = 7;
// 加载
void download()
{std::cout << "This is a downlaod task!" << std::endl;
}
// 打印
void print()
{std::cout << "This is a print task!" << std::endl;
}
// 刷新
void flush()
{std::cout << "This is a flush task!" << std::endl;
}std::vector<task_t> task;void load_task()
{task.push_back(download);task.push_back(print);task.push_back(flush);
}
void excute_task(int option_task)
{if (option_task < 0 || option_task >= task.size())return;task.at(option_task)();
}void work(int rfd)
{int t = task_number;int option_task = 0;int n = read(rfd, &option_task, sizeof(option_task));if (n > 0){excute_task(option_task);std::cout << "--------------------------------" << std::endl;}
}
Makefile
process_pool:process_pool.ccg++ $^ -o $@ -std=c++11.PHONY:clean
clean:rm -rf process_pool
谢谢大家!
相关文章:
Linux 进程间通信之管道
个人主页:仍有未知等待探索-CSDN博客 专题分栏: Linux 目录 一、通信 1、进程为什么要通信? 1.数据的类型 2.父进程和子进程算通信吗? 2、进程如何通信? 3、进程通信的常见方式? 二、管道 1、概念…...
IDEA 无法启动,点击之后没有任何提示或者界面
当你尝试通过双击或以管理员身份启动程序时,均未能成功,且未收到任何提示信息或界面反馈,这确实令人困扰。为了诊断问题并获取有用的错误信息,你可以按照以下步骤操作: 1. 启用并查看错误信息 首先,你需要…...
ctf 堆栈结构
CTF(Capture The Flag)竞赛中,理解堆栈结构对于解决涉及二进制分析、逆向工程和利用开发的挑战至关重要。堆栈是在程序执行过程中用于临时存储数据和管理函数调用的关键数据结构。以下是堆栈结构的基本概念及其在CTF竞赛中的应用:…...
sqlserver的openquery配置
1.命令Demo ---openquery--开启Ad Hoc Distributed Queries组件,在sql查询编辑器中执行如下语句exec sp_configure show advanced options,1reconfigureexec sp_configure Ad Hoc Distributed Queries,1reconfigure--关闭Ad Hoc Distributed Queries组件࿰…...
Spring boot logback日志框架加载初始化源码
##LoggingApplicationListener监听 Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApplicationStartingEvent((ApplicationStartingEvent) event);}else if (event instanceof ApplicationEnvironment…...
qt-11基本对话框(消息框)
基本对话框--消息框 msgboxdlg.hmsgboxdlg.cppmain.cpp运行图QustionMsgInFormationMsgWarningMsgCriticalMsgAboutMsgAboutAtMsg自定义 msgboxdlg.h #ifndef MSGBOXDLG_H #define MSGBOXDLG_H#include <QDialog> #include <QLabel> #include <QPushButton>…...
Windows11下wsl闪退的解决
wsl闪退 1. 原因分析 解释:WSL(Windows Subsystem for Linux)闪退通常指的是在Windows操作系统中运行的Linux环境突然关闭。这可能是由于多种原因造成的,包括系统资源不足、WSL配置问题、兼容性问题或者是Linux内核的问题。&…...
通过调整JVM的默认内存配置来解决内存溢出(OutOfMemoryError)或栈溢出(StackOverflowError)等错误
文章目录 引言I 调整JVM的默认堆内存配置java命令启动jar包Tomcat服务器部署java应用引言 问题: org.springframework.web.util.estedServletException: Handlerdispatch failed: nested exception isjava.lang.0utOfMemoryError: Java heap space原因分析: 查询查询平台所…...
RCE---eval长度限制绕过技巧
目录 题目源码 方法一:命令执行的利用 方法二:file_put_contents(本地文件包含的利用) 方法三:usort(…$_GET); 题目源码 <?php $param $_REQUEST[param]; if(strlen($param)<17 && stripos($par…...
C++11标准模板(STL)- 算法库 - 类似 std::accumulate,但不依序执行 -(std::reduce)
算法库 算法库提供大量用途的函数(例如查找、排序、计数、操作),它们在元素范围上操作。注意范围定义为 [first, last) ,其中 last 指代要查询或修改的最后元素的后一个元素。 类似 std::accumulate,但不依序执行 std…...
反射机制的介绍
什么是反射 Java反射机制是Java语言一个很重要的特性,它使得Java具有了“动态性”。在Java程序运行时,对于任意的一个类,我们能不能知道这个类有哪些属性和方法呢?对于任意的一个对象,我们又能不能调用它任意的方法&a…...
AI图文带货,手把手教学,傻瓜操作,轻松日入500+,小白教程
通过自媒体的力量,帮助普通人成为企业家。 建立自己的财富事业,用你的影响力帮助更多的人。 从而实现你更加自由的生活方式。 记住关注我,不要错过每一次分享。 对标账号 作为公司的一个项目实际拆解者,最热门的项目怎么能不拆…...
java:实现简单的验证码功能
效果 实现思路 验证码图片的url由后端的一个Controller生成,前端请求这个Controller接口的时候根据当前时间生成一个uuid,并把这个uuid在前端使用localStorage缓存起来,下一次还是从缓存中获取。 Controller生成验证码之后,把前…...
MybatisPlus使用指南
MybatisPlus 1. 快速入门1.1 入门案例1.2 常见注解1.3 常见配置 2. 核心功能2.1 条件构造器2.2 自定义SQL2.3 Service接口 3. 扩展功能3.1 代码生成3.2 静态工具3.3 逻辑删除 4. 插件功能4.1 分页插件4.2 通用分页实体 1. 快速入门 1.1 入门案例 步骤一:引入Mybat…...
5. MongoDB 集合创建、更新、删除
1. 创建集合 1.1 语法 db.createCollection(name, options) 参数说明: name: 要创建的集合名称。options: 可选参数, 指定有关内存大小及索引的选项。 options 可以是如下参数: 参数名类型描述示例值capped布尔值是否创建一个固定大小的集合。truesize…...
PHP中如何将变量从函数传递给acf_add_filter
在PHP开发中,我们有时需要将变量从函数传递给acf的add_filter钩子。这样做可以让我们在acf字段加载时,对字段值进行动态修改。下面,我将详细介绍如何实现这一功能。 在acf中,我们使用add_filter来添加钩子,对字段的加…...
KNN算法的使用
目录 一、KNN 算法简介 二、KNN算法的使用 1.读取数据 2.处理数据 三、训练模型 1.导入KNN模块 2.训练模型 3.出厂前测试 四、进行测试 1.处理数据 2.进行测试 总结 一、KNN 算法简介 KNN 是一种基于实例的学习算法。它通过比较样本之间的距离来进行预测。算法的核心…...
java文件上传
导入jar包,或者maven <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>…...
MySQL 数据库经验总结
一、数据库操作 1. 创建数据库 CREATE DATABASE database_name;例如,创建一个名为 my_database 的数据库: CREATE DATABASE my_database;2. 选择数据库 USE database_name;要使用刚才创建的 my_database 数据库: USE my_database;3. 删除…...
Python环境安装及PIP安装(Mac OS版)
官网 https://www.python.org/downloads/ 安装python python-3.12.1-macos11.pkg下载后,安装一直下一步即可 验证是否安装成功,执行python3命令和pip3命令 配置环境变量 获取python3安装位置并配置在.bash_profile #查看python路径 which python3#…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...










