『Linux从入门到精通』第 ㉓ 期 - 管道

在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
  • 🐧进程间通信的目的
  • 🐧如何进行进程间通信
  • 🐧进程间通信的分类
  • 🐧管道
    • 🐦什么是管道
    • 🐦管道原理
  • 🐧实例代码
  • 🐧管道的特点
  • 🐧代码拓展 - 通过管道实现进程控制

💐专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 Linux从入门到精通,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法

💐文章导读

本章我们将深入学习如何通过管道进行进程间通信。

在这里插入图片描述

🐧进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程;
  • 资源共享:多个进程之间共享同一份资源;
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件(如进程终止时要通知父进程);
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截一个进程所有陷入和异常,并能够及时知道它的状态改变;

🐧如何进行进程间通信

首先进程间进行通信有最大的一个难题:

  • 进程是具有独立性的

也就是说,我们无法做到让两个进程之间访问彼此的资源。因此进程间通信的首要任务是

  • 让两个进程看到同一份资源
  • 然后让一方写入,另一方进行读取

这也是主流的我们将要的介绍的几种进程间通信的方法的核心思想。

🐧进程间通信的分类

进程间通信经过漫长的发展,逐渐衍生出以下几种方式:

管道

  • 匿名管道(pipe);
  • 命名管道;

System V IPC

  • System V 消息队列;
  • System V 共享内存;
  • System V 信号量;

POSIX IPC

  • 消息队列;
  • 共享内存;
  • 信号量;
  • 互斥量;
  • 条件变量;
  • 读写锁;

本章节我们将介绍管道的进程间通信方式。

🐧管道

🐦什么是管道

在不久之前我们应该都见过Linux中的管道,例如:

在 myfile.txt 中写入五行文字:

$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ cat myfile.txt 
hello world
hello world
hello world
hello world
hello world

当我们想统计出 myfile.txt 中有几行文字时:

$ cat myfile.txt | wc -l
5
  • wc -l 用来统计文本中的行数;

在Linux中,"管道"是一种机制,允许将一个命令的输出直接传递给另一个命令作为输入。这个机制通过竖线符号|来实现。通过使用管道,你可以将一个命令的输出作为另一个命令的输入,从而实现多个命令的协同工作,形成一个命令链。

例如,假设你有两个命令:command1 和 command2。你可以使用管道将它们连接在一起,使 command2 处理 command1 的输出。命令的形式如下:

$ command1 | command2

这会将 command1 的输出传递给 command2,而不是将其打印到终端。这种机制使得在Linux系统上可以轻松地组合和重用命令,从而实现更复杂的操作。

在计算机领域中,管道是一个比较大的概念,Linux指令中的 | 是管道的一种形式。

🐦管道原理

我们首先要明确,管道是一个文件。要做到让两个进程看到同一份资源,说明这一份资源不独属于任何一个进程。

当我们创建一个进程,该进程会拥有自己的tast_struct结构体,在这个结构体中管理着 struct files_struct字段,files_struct中又管理着该进程的文件描述符表 (struct file* fd_array[]),如下图所示:

在这里插入图片描述

在该进程中,分别用读方式与写方式打开一个文件;然后 fork 创建子进程,此时子进程会继承来自父亲的与父亲相同的文件描述符表;

在这里插入图片描述

此时父进程与子进程指向了同一个文件,接下来结合实际场景,例如,我们想让父进程进行写入,让子进程负责读取,这时父子进程需要关闭不需要的文件描述符,父进程将 4 关闭,子进程将 3 关闭,这时父子进程就可以根据需要来收发数据了。
在这里插入图片描述

🐧实例代码

现在有一个需求:子进程向管道中发送数据,父进程每隔一秒读取一次管道中的内容。

在代码的编写过程中,我们需要用到一个接口——pipe为我们生成管道文件。它的基本原型如下:

#include <unistd.h>int pipe(int pipefd[2]);

这里,pipefd 是一个整型数组,有两个元素,分别表示管道的两个端口。pipefd[0] 代表读取端口pipefd[1] 代表写入端口。成功调用 pipe 函数后,这两个文件描述符将被用于在两个相关进程之间传递数据。

#include <iostream>
#include <iostream>
#include <string>
#include <cerrno>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>using namespace std;int main()
{int pipefd[2] = {0};// 1. 创建管道int n = pipe(pipefd);if (n < 0){cout << "pipe error" << errno << ": " << strerror(errno) << endl;return 1;}cout << "pipfd[0]" << pipefd[0] << endl;cout << "pipfd[1]" << pipefd[1] << endl;// 2.创建子进程pid_t id = fork();assert(id >= 0);if(id == 0) // 子进程{// 3.关闭不需要的fdclose(pipefd[0]);// 4.开始通信const string namestr = "我是子进程";int cnt = 1;char buffer[1024];while(true){snprintf(buffer, sizeof buffer, "%s : %d, pid : %d\n", namestr.c_str(), cnt++, getpid());write(pipefd[1], buffer, strlen(buffer));}close(pipefd[1]);exit(0);}// 父进程close(pipefd[1]);// 4.开始通信char buffer[1024];int cnt = 0;while(true){int n = read(pipefd[0], buffer, sizeof(buffer) - 1);if(n > 0){cout << "我是父进程,我读到了信息:\n" << buffer << endl;}else if(n == 0){cout << "我是父进程,我读到了文件末尾" << buffer << endl;break;}else{cout << "我是父进程,读取异常" << buffer << endl;break;} sleep(1);}return 0;
}

🐧管道的特点

在管道的使用中,我们经常能遇到以下四种场景:

  1. 当我们read读取完毕所有的管道数据,如果写端不再继续写入数据,那读端只能等待;
  2. 当写端将管道全部写满了之后(管道文件的容量是有上限的)就不能在继续写入数据了;
  3. 当写端将数据全部写入并退出后,读端读取完毕管道数据后,read就会返回0,代表读到了文件末尾;
  4. 当写端一直写入数据,而读端关闭,这时写端所作的事情是无意义的,OS此刻会杀死一直写入数据的进程,OS会通过发送信号来终止该进程;

由四种场景,我们可以总结出管道具有的特点:

  1. 管道是单向通信的(半双工)。管道并不能读取和写入同时进行;
  2. 管道的生命周期随进程,当进程退出时,管道释放;
  3. 管道通信经常是由具有“血缘关系”的进程间进行的,例如父子进程;
  4. 在管道通信中,读与写的次数并不是强相关的,因为管道提供的是流式服务;
  5. 管道具有一定的系统能力,让读端与写端能够按照一定的步骤进行通信;

🐧代码拓展 - 通过管道实现进程控制

接下来,我们就基于管道进行一个简单的设计——父进程向不同的子进程写入特定的消息,唤醒子进程,并让子进程去执行特定的命令。

/* ctrlProcess.cpp */#include "Task.hpp"using namespace std;Task t;
const int gnum = 3;class EndPonit
{
public:EndPonit(int id, int fd): _child_id(id),_write_fd(fd){}~EndPonit(){}public:pid_t _child_id; // 子进程idint _write_fd;   // 写端fd
};// 子进程要执行的方法
//void WaitCommand(int _write_fd)
void WaitCommand()
{while (true){int command = 0;int n = read(_write_fd, &command, sizeof(int));if (n == sizeof(int)){t.Execute(command);}else if (n == 0){break;}else{break;}}
}void creatProcess(vector<EndPonit> *end_points)
{for (int i = 0; i < gnum; i++){// 1.创建管道int pipefd[2] = {0};int n = pipe(pipefd);assert(n == 0);(void)n;// 2.创建进程pid_t id = fork();assert(id != -1);// 子进程if (id == 0){// 3.关闭不要的fdclose(pipefd[1]);// 输入重定向dup2(pipefd[0], 0);// 子进程等待获取命令WaitCommand();//WaitCommand(pipefd[0]);close(pipefd[0]);exit(0);}// 父进程// 关闭不要的fdclose(pipefd[0]);// 4.将新的子进程和它的管道写端,构建对象end_points->push_back(EndPonit(id, pipefd[1]));}
}
int main()
{vector<EndPonit> end_points;creatProcess(&end_points);int num = 0;while (true){// 1.选择任务int command = COMMAND_FUNC1;// 2.选择进程int index = rand() % end_points.size();// 3.下发任务write(end_points[index]._write_fd, &command, sizeof(int));sleep(1);}return 0;
}
/* Task.hpp */
#pragma once#include <iostream>
#include <vector>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
using namespace std;typedef void (*func_t)();void Func1()
{cout << "pid:" << getpid() << "Func1...." << endl;
}void Func2()
{cout << "pid:" << getpid() << "Func2...." << endl;
}void Func3()
{cout << "pid:" << getpid() << "Func3...." << endl;
}#define COMMAND_FUNC1 0
#define COMMAND_FUNC2 1
#define COMMAND_FUNC3 2class Task
{
public:Task(){_tasks.push_back(Func1);_tasks.push_back(Func2);_tasks.push_back(Func3);}void Execute(int command){if (command >= 0 && command < _tasks.size())_tasks[command]();}~Task(){}private:vector<func_t> _tasks;
};

运行效果展示

在这里插入图片描述

本章的内容到这里就结束了!如果觉得对你有所帮助的话,欢迎三连~

在这里插入图片描述

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

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

相关文章

Window系统部署Splunk Enterprise并结合内网穿透实现远程访问本地服务

文章目录 前言1. 搭建Splunk Enterprise2. windows 安装 cpolar3. 创建Splunk Enterprise公网访问地址4. 远程访问Splunk Enterprise服务5. 固定远程地址 前言 本文主要介绍如何简单几步&#xff0c;结合cpolar内网穿透工具实现随时随地在任意浏览器&#xff0c;远程访问在本地…

【24最新版PythonPycharm安装教程】小白保姆级别安装教程

今天&#xff0c;我就来教大家一下&#xff0c;如何去安装Python&#xff01; 需要博主打包好的一键激活版Pycharm&&Python也可扫下方直接获取 ​ 1 了解Python Python是一种面向对象的解释型计算机程序设计语言&#xff0c;由荷兰人Guido van Rossum于1989年发明&…

[C++]使用纯opencv去部署yolov9的onnx模型

【介绍】 部署 YOLOv9 ONNX 模型在 OpenCV 的 C 环境中涉及一系列步骤。以下是一个简化的部署方案概述&#xff0c;以及相关的文案。 部署方案概述&#xff1a; 模型准备&#xff1a;首先&#xff0c;你需要确保你有 YOLOv9 的 ONNX 模型文件。这个文件包含了模型的结构和权…

Flutter Gradle下载失败的解决方案

Flutter Gradle可能会由于网络原因下载失败,这个时候我们可以首先下载Gradle&#xff0c;然后再进行配置。具体步骤如下&#xff1a; 第一步&#xff1a;下载对应版本的gradle 可以通过下面地址下载&#xff0c;也可以百度里面搜对应的版本 【极速下载】gradle各版本快速下载地…

【HTML】HTML基础2(一些常用标签)

目录 例子 首先是网页图标 然后是一些常用标签 插入图片 例子 <!DOCTYPE html> <html><head><link rel"icon" href"img/银河护卫队-星爵.png" type"image/x-icon"><meta charset"utf-8"><title>…

如何限制一个账号只在一处登陆

大家好&#xff0c;我是广漂程序员DevinRock&#xff01; 1. 需求分析 前阵子&#xff0c;和问答群里一个前端朋友&#xff0c;随便唠了唠。期间他问了我一个问题&#xff0c;让我印象深刻。 他问的是&#xff0c;限制同一账号只能在一处设备上登录&#xff0c;是如何实现的…

【大厂AI课学习笔记NO.56】(9)模型评测

作者简介&#xff1a;giszz&#xff0c;腾讯云人工智能从业者TCA认证&#xff0c;信息系统项目管理师。 博客地址&#xff1a;https://giszz.blog.csdn.net 声明&#xff1a;本学习笔记来自腾讯云人工智能课程&#xff0c;叠加作者查阅的背景资料、延伸阅读信息&#xff0c;及学…

Python采集学习笔记-request的get请求和post请求

使用http://httpbin.org测试,一个简单的 HTTP 请求和响应服务。(需联网)1.导入requests包 import requests 2.测试get请求 url http://httpbin.org/get par {key1: value1, key2: value2} # 不带参数请求 r1 requests.get(url) # 带参数请求 r2 requests.get(url, paramspa…

甘特图资源视图和任务视图的区别

甘特图(Gantt chart)是一种常用的项目管理工具,用于直观地展示项目的进度和各项任务的时间安排。甘特图包含资源视图和任务视图两种视角。 一个项目的甘特图demo &#xff1a; https://zz-plan.com/share/87f1340286f1343ba5 资源视图主要显示项目中不同资源的分配和利用情况…

植物神经功能紊乱患者,家属应该怎么照顾!

植物神经功能紊乱181-01-317-367&#xff0c;通常被称为植物人状态&#xff0c;是指由严重脑部受损引起的意识丧失、无法自主呼吸、难以醒来或维持清醒状态的一种严重健康状况。植物神经功能紊乱患者通常面临长期卧床、意识不清、无法言语交流等严重问题&#xff0c;需要家属的…

【内推】金山办公 2024届 春季校园招聘

有需要内推的小伙伴吗&#xff1f; 金山办公 各岗位均有 面向应届生春招 QQ群&#xff1a;723529936 内推码&#xff1a;NTASYQI

海外代购系统独立站,商品采集API接口系列

海外代购系统独立站是一个完整的电商平台&#xff0c;专为代购业务设计。这样的系统通常具备商品采集、库存管理、订单处理、支付集成、物流追踪等功能。其中&#xff0c;商品采集是整个系统的基础&#xff0c;而API接口是实现商品采集的关键。 请求示例&#xff0c;API接口接…

使用OpenTelemetry进行监控

工具介绍 注意&#xff1a;该部分介绍摘抄自&#xff1a;搭建高级的性能监控系统(PrometheusGrafanaNode ExporterAlertmanager) - 爱云 Prometheus、Grafana、Node Exporter 和Alertmanager是一组用于监控和可视化系统性能的开源工具。它们通常一起使用&#xff0c;形成一个强…

大地测量学课堂笔记:1、绪论

慕课网址&#xff1a;https://www.icourse163.org/course/WHU-1464124180?fromsearchPage&outVendorzw_mooc_pcssjg_https://www.icourse163.org/course/WHU-1464124180?fromsearchPage&outVendorzw_mooc_pcssjg_ 1. 大地测量学的定义 大地测量学是专门研究精确测量…

【C++精简版回顾】18.文件操作

1.文件操作头文件 2.操作文件所用到的函数 1.文件io 1.头文件 #include<fstream> 2.打开文件 &#xff08;1&#xff09;函数名 文件对象.open &#xff08;2&#xff09;函数参数 /* ios::out 可读 ios::in 可…

使用华为云云函数functiongraph

之前使用腾讯云serverless&#xff0c;但是突然开始收费了。所以改用functiongraph 首先登陆华为云。 目录 1.登录华为云 2.在控制台找到functiongraph并开通 3.添加依赖包&#xff1a; 3.1 制作依赖包 3.2引入依赖包 4.发送请求 4.1直接发送 4.1.1uri 4.1.2 请求头…

“找不到msvcr90.dll无法启动软件如何解决

msvcr90.dll 是一个属于 Microsoft Visual C 2008 Redistributable Package 的动态链接库&#xff08;DLL&#xff09;文件。在Windows操作系统中&#xff0c;许多应用程序特别是那些使用Visual Studio 2008编译器开发的程序&#xff0c;在运行时可能需要调用这个库中的函数和资…

lua调用C++函数

第一步搭建lua的环境. win10 lua环境搭建-CSDN博客 我使用的环境是win10vs2015lua54 先来个最简单的lua调用C函数, 无参数无返回值的 第一步:定义C函数. int CTest(lua_State* L) // 返回值是固定的int类型,返回0表示没有返回参数,返回1表示有一个返回参数 {std::cout &l…

K8S高级篇:138页经典实战案例,图文并茂代码齐全,仅限3天分享

相信很多朋友都听过云原生和容器技术&#xff0c;当然也少不了K8S的大名&#xff0c;在“容器技术革命”中&#xff0c;K8S俨然已经成为容器技术的事实标准&#xff0c;各个知名互联网企业前仆后继地拥抱云原生&#xff0c;争先恐后地把容器和K8S作为战略重心之一。 容器技术发…

WordPress上传图片错误:不是合法的JSON响应

最近在进行WordPress迁移至新服务器的过程中&#xff0c;遭遇到一个棘手的问题&#xff0c;即在编辑文章并上传图片时&#xff0c;不断遭遇“此响应不是合法的JSON响应”的错误。经过多次验证和搜索&#xff0c;最终确定问题的根本原因并不在于禁用 Gutenberg 编辑器或安装经典…