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

谢谢大家!

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

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

相关文章

Java家教一对一系统小程序源码

&#x1f31f;【家教新风尚&#xff0c;一对一系统引领学习革命】&#x1f31f; &#x1f4da; 个性化定制&#xff0c;学习更高效 你还在为孩子的学习成绩提升缓慢而烦恼吗&#xff1f;家教一对一系统&#xff0c;正是解决这一难题的钥匙&#xff01;它根据孩子的年龄、兴趣…

动手学深度学习(pytorch)学习记录12-激活函数[学习记录]

激活函数 激活函数&#xff08;activation function&#xff09;通过计算加权和并加上偏置来确定神经元是否应该被激活&#xff0c; 它们将输入信号转换为输出的可微运算。 import torch import matplotlib.pyplot as plt 简单定义一个画图的函数 def graph_drawing(x_,y_…

static、extern,const关键字

1、static关键字 static关键字&#xff1a;延长生命周期&#xff0c;限制作用域 static修饰局部变量&#xff1a;静态局部变量 static修饰全局变量&#xff1a;静态全局变量 static修饰函数&#xff1a;静态函数 2、extern关键字 extern&#xff1a;引用其他文件 .c 中的全局…

私域场景中的数字化营销秘诀

​在当今的商业世界&#xff0c;私域场景的营销变得愈发重要。今天咱们就来深入探讨一下私域场景中的几个关键营销手段。 一、会员管理与营销 企业一旦拥有完善的会员体系&#xff0c;数字化手段就能大放异彩。它可以助力企业对会员进行精细划分&#xff0c;深度了解会员的消费…

STM32 GPIO 模块

B站视频地址&#xff1a;芯片内部GPIO模块细节 引脚 将 STM32 芯片&#xff0c;类比为【大脑】 而旁边的引脚&#xff0c;类比为【神经】 通过引脚&#xff0c;使得&#xff0c;STM32&#xff0c;可以和外部世界&#xff0c;进行交流 比如&#xff0c;当我们和别人说话时&am…

【安全工具推荐-Search_Viewer资产测绘】

目录 一、工具介绍 二、工具配置 三、传送门 一、工具介绍 Search_Viewer&#xff0c;集Fofa、Hunter鹰图、Shodan、360 quake、Zoomeye 钟馗之眼、censys 为一体的空间测绘gui图形界面化工具&#xff0c;支持一键采集爬取和导出fofa、shodan等数据&#xff0c;方便快捷查看…

基于808协议和1078协议的视频监控系统

卫星定位云服务平台 卫星定位云服务平台是一个车载视频终端监控系统,用于对卫星定位设备进行实时监控、实时定位、轨迹回放、指令下发、拍照记录、报警信息、实时视频、历史视频等功能。808协议和1078协议 内置功能 车队管理&#xff1a;车队信息的增删改查。型号管理&#…

快速MD5强碰撞生成器:fastcoll

问&#xff1a;可以制作两个具有相同哈希值的不同文件吗&#xff1f; 答&#xff1a;可以。 在密码学中&#xff0c;哈希函数将输入数据转换成固定长度的字符串。但由于输入的无限性和输出的固定性&#xff0c;不可避免地会有不同输入产生相同的哈希值&#xff0c;这就是碰撞。…

力扣面试经典算法150题:找出字符串中第一个匹配项的下标

找出字符串中第一个匹配项的下标 今天的题目是力扣面试经典150题中的数组的简单题: 找出字符串中第一个匹配项的下标 题目链接&#xff1a;https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/?envTypestudy-plan-v2&envIdto…

短视频SDK解决方案,降低行业开发门槛

美摄科技匠心打造了一款集前沿技术与极致体验于一体的短视频SDK解决方案&#xff0c;它不仅重新定义了短视频创作的边界&#xff0c;更以行业标杆级的短视频特效&#xff0c;让每一帧画面都闪耀不凡光芒。 【技术赋能&#xff0c;创意无限】 美摄科技的短视频SDK&#xff0c;…

基于DVWA-Brute Force(LowMedium)的渗透测试

Brute force主要是通过爆破达到渗透目的&#xff1a; Low 查看源代码&#xff1a; <?phpif( isset( $_GET[ Login ] ) ) {// Get username$user $_GET[ username ];// Get password$pass $_GET[ password ];$pass md5( $pass );// Check the database$query "SE…

遗传算法与深度学习实战(4)——遗传算法详解与实现

遗传算法与深度学习实战&#xff08;4&#xff09;——遗传算法详解与实现 0. 前言1. 遗传算法简介1.1 遗传学和减数分裂1.2 类比达尔文进化论 2. 遗传算法的基本流程2.1 创建初始种群2.2 计算适应度2.3 选择、交叉和变异2.4算法终止条件 3. 使用 Python 实现遗传算法3.1 构建种…

Adobe Audition AU 2023-23.6.6.1 解锁版下载和安装教程(专业的音频处理工具)

前言 Audition是Adobe旗下一款非常好用的音频处理工具&#xff0c;软件为用户们提供了功能强大的音频编辑功能和一个相对完善的工作流程&#xff0c;用户们无论是录制音乐、无线电广播还是视频配音&#xff0c;多音频合成&#xff0c;这款软件都能够给你足够的创作动力。audit…

7za解压缩工具

1、unzip无法解压缩大于4G的文件 从Windows平台通过MobaXterm上传一个大小约为5G的zip文件到AutoDL Linux系统上&#xff0c;使用unzip解压过程中出现如下错误&#xff1a; 从网上搜索了一下相关资料&#xff0c;发现是当前的unzip版本不支持4G以上的压缩包。要么升级到最新…

贪吃蛇(C语言详解)

贪吃蛇游戏运行画面-CSDN直播 目录 贪吃蛇游戏运行画面-CSDN直播 1. 实验目标 2. Win32 API介绍 2.1 Win32 API 2.2 控制台程序&#xff08;Console&#xff09; 2.3 控制台屏幕上的坐标COORD 2.4 GetStdHandle 2.5 GetConsoleCursorlnfo 2.5.1 CONSOLE_CURSOR_INFO …

60万奖池,CV算法+大模型创新应用双赛道——2024无锡国际人工智能创新应用大赛正式启动!

2024无锡国际人工智能创新应用大赛于8月15日开赛&#xff0c;本次大赛开放计算机视觉算法和大模型创新应用双赛道&#xff0c;召唤全球开发者、创新团队和企业实现人工智能技术创新与应用。 聚焦前沿&#xff0c;双重赛道 算法赛道参赛者将使用Jittor框架进行无人机视角下的算…

Wireshark显示过滤器常用关键字及过滤表达式

Wireshark显示过滤器常用关键字及过滤表达式 1. 过滤器类型 Wireshark抓包工具提供了两种类型过滤器&#xff1a;抓包过滤器 和 显示过滤器。 抓包过滤器&#xff1a; 抓取满足过滤条件的数据包&#xff0c;不满足过滤条件的数据包不会被抓取。 显示过滤器&#xff1a; 包已…

软件测试面试必杀篇:【2024软件测试面试八股文宝典】

800道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web测试、接口测试、App测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维、人力资源等模块面试题&am…

【计算机网络】TCP实战

其实有了UDP的基础&#xff0c;TCP不管怎么说学习起来都还是比较舒服的&#xff0c;至少是比直接就学习TCP的感觉好。 这篇文章最多就是介绍一下起手式&#xff0c;如果想带业务的话和UDP那篇是完全一样的&#xff0c;就不进行演示了。 总的来说还是很简单的。 目录 Echo服务端…

食品零食小吃商城管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…