Linux-管道通信

1. 管道概念        

        管道,是进程间通信的一种方式,在Linux命令中“ | ”就是一种管道,它可以,连接前一条命令,和后一条命令,把前面命令处理完的内容交给后面,例如

cat filename | grep hello

        cat命令会把文件的内容打印在显示器上,而管道就把这些内容交给后面的 grep 命令,由 grep 处理过后再把内容打印到显示器上

        这就是管道,这两个命令可以看作,两个进程,管道负责把内容传输由一方传给另一方

        这也同时注定了,管道是一份共享的资源,被两个进程同时看到,并使用。

        再之前的学习中,我们有没有两个进程共享同一份资源的呢?

        有,fork创建子进程,父子进程同时打印数据到显示器上,这是不是也是一种资源的共享,这是利用文件系统,当父进程创建子进程,子进程会拷贝父进程的大部分内容,包括PCB(task_struct),代码和数据,以及文件描述符表files_struct,子进程会复制父进程对应的file*指针,这也就导致他们指向同一个文件,所以他们可以同时向屏幕写数据。

b1b40997dc984a22b489c503bbb4e7fa.png

2. 匿名管道

        基于上面的启发,对于父子进程之间,我们是不是也可以通过打开同一份文件,一方读,另一方写,进而达成通信的目的。

具体步骤:

        1. 父进程先以读写的方式打开同一份文件

        2. fork创建子进程

        3. 父进程关闭读端,子进程关闭写端     

2b911205d2d64a9189fe42e8cfa1761c.png

        这样就形成了一个管道,父进程负责向管道写数据,子进程负责向管道读数据

        但是单纯是文件,还不行,因为文件会有IO操作,如果内存文件和磁盘进行读写,会大大降低效率,而管道通信,只需要一个临时的内存级文件,不需要向磁盘写入和读取,所以这就需要系统提供的pipe函数

574ebfcd7f2641b2922546490f65dda7.png

3fe115aa9af54eed8619992b07750861.png

        pipe会创建一个管道,用于进程间通信,他会打开并创建一个内存级的临时文件,其中参数是一个输出型参数,需要自己定义一个数组,函数调用后,pipefd[0]是进程的读方式打开文件返回的fd,pipefd[1]是写端。返回值,0标识成功,-1标识失败。

        这就是匿名管道。

        我们可以发现匿名管道有以下几个特点

  • 匿名管道建立的基础是具有血缘关系的进程,来完成进程间通信,父子进程
  • 匿名管道,所以他的生命周期随着进程,当没有进程再打开他,他就会被删除
  • 管道的本质是文件,管道是基于文件的读写,read和write都是面向流的,面向字节流
  • 管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道
  • 管道还具有访问控制,通过让进程间协同,访问控制主要体现在:
  • a. 当写快,读慢,管道被写满了就不会再写,也就是写的进程等待,本质是阻塞
  • b. 当写慢,读快,管道没有数据时,就不会再读,也是阻塞式的等待
  • c. 当写关闭,读0,读到0,read的返回值为0代表读到文件结尾
  • d. 当读关闭,写继续写,OS就会终止写方的进程SIGPIPE

匿名管道间通信的实验:

process.cc

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"using namespace std;#define Child_Process_Size 5int command_wait(int fd, bool &flag)
{uint32_t command = 0;ssize_t sz = read(fd, &command, sizeof(command));// cout << "command:" << command << endl;// cout << "sz:" << sz << endl;if (sz == 0)flag = true;elseassert(sz == sizeof(command));return command;
}void command_push(pid_t child_process, int fd, int command)
{cout << "main:child_process" << child_process << "\tfd" << fd << "\tcommand" << command << endl;ssize_t ret = write(fd, &command, sizeof(command));// cout << ret << "!" << endl;
}int main()
{load();vector<pair<pid_t, int>> slots;for (int i = 0; i < Child_Process_Size; ++i){// 建立管道int pipefd[2] = {0};int n = pipe(pipefd);assert(n == 0);(void)n;pid_t id = fork();assert(id != -1);if (id == 0){// child// 关闭信道close(pipefd[1]);while (true){bool flag = false;int command = command_wait(pipefd[0], flag);// cout << command << "#" << endl;// printf("%p888888\n", callbacks[command]);if (flag)break;if (command >= 0 && command < callbacks.size()){callbacks[command]();}else{cout << "非法command:" << command << endl;}}exit(1);// 执行command}// fatherclose(pipefd[0]);slots.push_back(pair<pid_t, int>(id, pipefd[1]));}// 派发任务srand((unsigned int)time(nullptr));int time = 5;随机派发// while(time--)// {//     int command = rand() % callbacks.size();//     int child = rand() % Child_Process_Size;//     command_push(slots[child].first, slots[child].second, command);//     sleep(1);// }指定派发while (true){cout << "******************************" << endl;cout << "** 1. describe   2. command **" << endl;cout << "*********  0. quit  **********" << endl;cout << "please cin your choice:";int child = rand() % Child_Process_Size;int command;bool quit = false;int choice = -1;cin >> choice;switch (choice){case 1:describe();break;case 2:cout << "please cin command:";cin >> command;command_push(slots[child].first, slots[child].second, command);break;case 0:quit = true;break;default:cout << "please recin:";break;}if (quit)break;}for (const auto &e : slots){close(e.second);}for (const auto &e : slots){pid_t ret = waitpid(e.first, nullptr, 0);assert(ret >= 0);}return 0;
}

 task.hpp

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <unistd.h>using namespace std;typedef std::function<void()> func;unordered_map<int, string> desc;
vector<func> callbacks;void mystrcpy()
{cout <<"child process" << getpid() << "字符串拷贝" << endl;
}void mystrstr()
{cout <<"child process" << getpid() <<  "字符串匹配" << endl;
}void mystrcmp()
{cout <<"child process" << getpid() <<  "字符串比较" << endl;
}void mystrlen()
{cout <<"child process" << getpid() <<  "字符串计数" << endl;
}void load()
{desc.insert({callbacks.size(), "mystrcpy:字符串拷贝"}); callbacks.push_back(mystrcpy);desc.insert({callbacks.size(), "mystrstr:字符串匹配"});    callbacks.push_back(mystrstr);desc.insert({callbacks.size(), "mystrcmp:字符串比较"});      callbacks.push_back(mystrcmp);desc.insert({callbacks.size(), "mystrlen:字符串计数"});callbacks.push_back(mystrlen);
}void describe()
{for(const auto& e : desc){cout << e.first << "\t" << e.second << endl;}
}

3. 命名管道

        命名管道(IFO)是基于匿名管道(pipe)的的更进一步,匿名管道只能在有血缘关系的进程间通信,而命名管道没有这个限制。

        创建命名管道有两种方式:

        a. 命令行

mkfifo name_fifo

e38b5c6271fd4579bc548d3909a804c4.png         命名管道文件属性为p,文件没有内容,也是内存级文件,他只有inode属性,没有data block数据块,数据都在内存

        b. 函数

04fb1877fcf54a8dad567283aa0f224f.png

        参数pathname文件路径,你需要把命名管道文件创建在那个路径下,mode文件属性,mode&~umask为最终文件属性,例如0666,最终文件属性664(rw_rw_r__),返回值0成功,-1失败

        命名管道和匿名管道在使用上的区别,匿名管道由pipe函数创建并打开命名管道由mkfifo函数创建,打开用open

        匿名管道的删除不需要额外动作,进程结束,管道就没了。但命名管道不同,命名管道需要删除,他是存在磁盘上的,只有属性没有内容。两种方式:

        a. 命令行

rm name_fifo
unlink name_fifo

        b. 函数

6b4fc338d0af40a9bf54239da8ab866a.png

        匿名管道和命名管道,本质都是文件,除了上面的一些区别,其他的使用都是一样的,而且也是具有访问控制,都是文件嘛!

关于命名管道的实验:

head.hpp

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <cstdlib>
#include <ctime>
#include "log.hpp"using namespace std;#define SIZE 1024
#define MODE 0666string ipcPath = "./fifo";

log.hpp

#pragma once#include <ctime>
#include <iostream>
#include <string>enum status
{Debug,Notice,Warning,Error
};const std::string str[] = {"Debug","Notice","Warning","Error"
};std::ostream& log(std::string message, status level)
{std::cout << str[level] << (unsigned int)time(nullptr) << ":" << message;return std::cout;
}

client.cc

#include "head.hpp"int main()
{int fd = open(ipcPath.c_str(), O_WRONLY);if(fd < 0){perror("open");exit(1);}string message;while(true){cout << "client[" << getpid() << "]:";cin >> message;write(fd, message.c_str(), message.size());sleep(1);}close(fd);return 0;
}

server.cc

#include "head.hpp"void read_fifo(int fd)
{char buffer[SIZE];while(true){memset(buffer, '\0', SIZE);ssize_t sz = read(fd, buffer, SIZE - 1);if(sz > 0){buffer[sz] = '\0';cout << "server[" << getpid() << "]:" << buffer << endl;}else if(sz == 0){break;}}
}int main()
{int ret = mkfifo(ipcPath.c_str(), MODE);if(ret == -1){perror("mkfifo");exit(1);}log("创建命名管道成功", Debug) << "step 1" << endl;int fd = open(ipcPath.c_str(), O_RDONLY);if(fd < 0){perror("open");exit(2);}log("以读方式打开管道成功", Debug) << "step 2" << endl;for(int i = 0; i < 3; ++i){int _fd = fork();if(_fd < 0){perror("fork");exit(3);}if(_fd == 0){//childread_fifo(fd);exit(1);}}for(int i = 0; i < 3; ++i){pid_t ret = waitpid(-1, nullptr, 0);if(ret > 0){string str = "等到子进程:";string id = to_string(ret);log(str+id, Notice);}}   close(fd);log("关闭打开的管道", Debug) << "step 3" << endl;unlink(ipcPath.c_str());log("删除管道文件成功", Debug) << "step 4" << endl;return 0;
}

完。

 

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

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

相关文章

IDEA 中的奇技淫巧

IDEA 中的奇技淫巧 书签 在使用ctrlalt方向键跳转时&#xff0c;或者追踪代码时&#xff0c;经常遇到的情况是层级太多&#xff0c;找不到代码的初始位置&#xff0c;入口。可以通过书签的形式去打上一个标记&#xff0c;后续可以直接跳转到书签位置。 标记书签&#xff1a;c…

C# GetField 方法应用实例

目录 关于 C# Type 类 GetField 方法应用 应用举例 心理CT设计题 类设计 DPCT类实现代码 小结 关于 C# Type 类 Type表示类型声明&#xff1a;类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义&#xff0c;以及开放或封闭构造的泛型类型。调用 t…

新媒体运营-----短视频运营-----PR视频剪辑----视频调色

新媒体运营-----短视频运营-----PR视频剪辑-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/138079659 文章目录 1. Lumetri调色&#xff0c;明暗对比度2. Lumetri调色&#xff0c;创意与矢量示波器2.1 创意2.2 矢量示波器 3. L…

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

纯js对比excel小工具

如何使用JavaScript和xlsx.js实现Excel文件对比&#xff1a;实战指南 在日常办公或数据分析工作中&#xff0c;我们经常需要比较两个Excel文件中的数据差异。手动对比不仅耗时费力&#xff0c;还容易出错。本文将带你通过一个简单的网页应用&#xff0c;利用JavaScript和开源库…

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

GPT学术优化推荐(gpt_academic )

GPT学术优化 (GPT Academic):支持一键润色、一键中英互译、一键代码解释、chat分析报告生成、PDF论文全文翻译功能、互联网信息聚合GPT等等 ChatGPT/GLM提供图形交互界面&#xff0c;特别优化论文阅读/润色/写作体验&#xff0c;模块化设计&#xff0c;支持自定义快捷按钮&…

[iOS]CocoaPods安装和使用

1.了解brew、rvm、ruby、gem、cocaspods之间的关系 在 macOS 环境中&#xff0c;Brew、RVM、Ruby、Gem 和 CocoaPods 之间存在以下关系&#xff1a; Homebrew (Brew)&#xff1a;Homebrew 是 macOS 上的包管理器&#xff0c;用于安装和管理各种开源软件包。它使您能够轻松地从…

基于SpringBoot+Vue校园竞赛管理系统的设计与实现

项目介绍&#xff1a; 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;竞赛信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行…

【AIGC调研系列】Sora级别的国产视频大模型-Vidu

Vidu能够达到Sora级别的标准。Vidu被多个来源认为是国内首个Sora级别的视频大模型[2][3][4]。它采用了团队原创的Diffusion与Transformer融合的架构U-ViT&#xff0c;能够生成长达16秒、分辨率高达1080P的高清视频内容[1][6]。此外&#xff0c;Vidu的一致性、运动幅度都达到了S…

HEVC/H.265视频编解码学习笔记–框架及块划分关系

前言 由于本人在学习视频的过程中&#xff0c;觉得分块单元太多搞不清楚其关系&#xff0c;因此本文着重记录这些分块单元的概念以及关联。 一、框架 视频为一帧一帧的图像&#xff0c;其编码的主要核心是压缩空间以及时间上的冗余。因此&#xff0c;视频编码有帧内预测和帧间…

使用docker搭建GitLab个人开发项目私服

一、安装docker 1.更新系统 dnf update # 最后出现这个标识就说明更新系统成功 Complete!2.添加docker源 dnf config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo # 最后出现这个标识就说明添加成功 Adding repo from: https://download.…

uniapp分包,以及通过uni-simple-router进行分包

先说一下uniapp的直接分包方式&#xff0c;很简单&#xff1a; 配置分包信息 打开manifest.json源码视图&#xff0c;添加 “optimization”:{“subPackages”:true} 开启分包优化 我们在根目录下创建一个pagesA文件夹&#xff0c;用来放置需要分包的页面 然后配置路由 运行到…

机器学习:基于Sklearn框架,使用逻辑回归对由心脏病引发的死亡进行预测分析

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

(八)Servlet教程——创建Web项目以及Servlet的实现

1. 打开Idea编辑器 2. 点击界面上的“新建项目”按钮 3. 设置好项目名称和位置 应用服务器选择之前设置好的Tomcat服务器 构建系统默认选择Maven 4. 点击“下一步”按钮 5. 点击“完成”按钮&#xff0c;Idea就创建好了项目&#xff0c;创建完成后的目录结构如下图所示 6. 此…

共享单车(二):项目日志

stdin, stdout, stderr Linux系统下&#xff0c;当一个用户进程被创建时&#xff0c;与之对应的三个数据流&#xff08;stdin&#xff0c;stdout和stderr&#xff0c;即三个文件&#xff09;也会被创建。 stdin&#xff0c;标准输入文件&#xff0c;通常对应着终端的键盘。 s…

将针孔模型相机 应用到3DGS

Motivation 3DGS 的 投影采用的是 CG系的投影矩阵 P P P, 默认相机的 principal point (相机光心) 位于图像的中点处。但是 实际应用的 绝大多数的 相机 并不满足这样一个设定&#xff0c; 因此我们 需要根据 f , c x , c y {f,c_x, c_y} f,cx​,cy​ 这几个参数重新构建3D …

docker安装【zookeeper】【kafka】【provectuslabs/kafka-ui】记录

目录 1.安装zookeeper:3.9.2-jre-172.安装kafka:3.7.03.安装provectuslabs/kafka-ui &#xff08;选做&#xff09;新环境没有jdk&#xff0c;安装jdk-17.0.10备用 mkdir -p /export/{data,apps,logs,conf,downloads}cd /export/downloadscurl -OLk https://download.oracle.…

Docker搭建Maven仓库Nexus

文章目录 一、简介二、Docker部署三、仓库配置四、用户使用Maven五、管理Docker镜像 一、简介 Nexus Repository Manager&#xff08;简称Nexus&#xff09;是一个强大的仓库管理器。 Nexus3支持maven、docker、npm、yum、apt等多种仓库的管理。 建立了 Maven 私服后&#xf…

中国发布首个汽车大模型标准

&#x1f989; AI新闻 &#x1f680; 中国发布首个汽车大模型标准 摘要&#xff1a;中国信息通信研究院于4月28日发布了国内首个汽车大模型标准&#xff0c;标志着汽车行业正式迈向“人工智能&#xff0b;”时代。该标准包含三个核心能力域&#xff1a;场景丰富度、能力支持度…