当前位置: 首页 > news >正文

【Linux-进程间通信】匿名管道+4种情况+5种特征

匿名管道

匿名管道(Anonymous Pipes)是Unix和类Unix操作系统中的一种通信机制,用于在两个进程之间传递数据。匿名管道通常用于命令行工具之间的数据传递;

匿名管道的工作原理是创建一个临时文件,该文件被称为管道文件,它仅存在于内存中,不持久化到磁盘。当一个进程创建一个匿名管道时,它会打开一个写入端和一个读取端。写入端通常由|运算符创建,而读取端则通过<运算符

匿名管道的一个关键特性是它是单向的,即只能从写入端到读取端传递数据。此外,一旦管道中的数据被读取,管道就会被关闭,不能再次使用。

【问题1】如果我想双向通信呢?两个管道

【问题2】为什么要单向通信?为了简单,因为我们想要单向通信,使用我们叫它管道

pipe

在C语言中,pipe函数是用于创建一个匿名管道(也称为管道)的标准库函数。它允许进程之间通过管道进行通信。pipe函数的声明如下:

 

这个函数的作用是在调用进程和其子进程之间创建一个匿名管道。pipefd是一个整数数组,包含两个整数元素,分别用于读取和写入管道。

  • pipefd[0]:这是管道的读取端,可以通过它从管道中读取数据。

  • pipefd[1]:这是管道的写入端,可以通过它将数据写入管道。

pipe函数的返回值:

  • 如果成功,pipe函数返回0。

  • 如果失败,pipe函数返回-1,并设置errno以指示错误。

成功调用pipe函数后,返回的两个文件描述符pipefd[0]pipefd[1]可以用于后续的读取和写入操作。

从上图可以看出,当需要进行通信时,需要通过pipefd[1]文件描述符,将数据拷贝到管道文件中;再通过pipefd[0]文件描述符,将管道文件中的数据拷贝到用户空间中。因而,管道通信时,需要产生两次拷贝

我们简单测试一下返回的文件描述符

testpipe.cc

#include <iostream>
#include <string>
#include <cerrno>  // errno.h
#include <cstring> // string.h
#include <unistd.h>int main(){//1.创建管道int pipefd[2];int n = pipe(pipefd);//输出型参数,rfd,wfdif(n != 0){std::cerr << "errno: " << errno << ": "<< "errstring : " << strerror(errno) << std::endl;return 1;}std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;return 0;
}

 

 接下来让子进程写入数据,父进程读数据,在此期间会关闭不需要的fd

#include <iostream>
#include <string>
#include <cerrno>  // errno.h
#include <cstring> // string.h
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>const int size = 1024;
std::string getOtherMessage()
{static int cnt = 0;std::string messageid = std::to_string(cnt); // stoi -> string -> intcnt++;pid_t self_id = getpid();std::string stringpid = std::to_string(self_id);std::string message = "messageid: ";message += messageid;message += " my pid is : ";message += stringpid;return message;
}// 子进程进行写入
void SubProcessWrite(int wfd)
{int pipesize = 0;std::string message = "father, I am your son prcess!";char c = 'A';while (true){std::string info = message + getOtherMessage(); // 这条消息,就是我们子进程发给父进程的消息write(wfd, info.c_str(), info.size()); // 写入管道的时候,没有写入\0, 有没有必要?没有必要std::cerr << info << std::endl;sleep(2); // 子进程写慢一点// write(wfd, &c, 1);// std::cout << "pipesize: " << ++pipesize << " write charator is : "<< c++ << std::endl;// // if(c == 'G') break;// sleep(1);}std::cout << "child quit ..." << std::endl;
}// 父进程进行读取
void FatherProcessRead(int rfd)
{char inbuffer[size]; // c99 , gnu g99while (true){//sleep(2);std::cout << "-------------------------------------------" << std::endl;// sleep(500);ssize_t n = read(rfd, inbuffer, sizeof(inbuffer) - 1); // sizeof(inbuffer)->strlen(inbuffer);if (n > 0){inbuffer[n] = 0; // == '\0'std::cout  << inbuffer << std::endl;}else if (n == 0){// 如果read的返回值是0,表示写端直接关闭了,我们读到了文件的结尾std::cout << "client quit, father get return val: " << n << " father quit too!" << std::endl;break;}else if(n < 0){std::cerr << "read error" << std::endl;break;}// sleep(1);break;}
}int main(){//1.创建管道int pipefd[2];int n = pipe(pipefd);//输出型参数,rfd,wfdif(n != 0){std::cerr << "errno: " << errno << ": "<< "errstring : " << strerror(errno) << std::endl;return 1;}std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;// 2. 创建子进程pid_t id = fork();if (id == 0){std::cout << "子进程关闭不需要的fd了, 准备发消息了" << std::endl;sleep(1);// 子进程 --- write// 3. 关闭不需要的fdclose(pipefd[0]);SubProcessWrite(pipefd[1]);close(pipefd[1]);exit(0);}// 3. 父进程读入数据// 关闭不需要的fdclose(pipefd[1]);FatherProcessRead(pipefd[0]);std::cout << "5s, father close rfd" << std::endl;sleep(5);close(pipefd[0]);int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){std::cout << "wait child process done, exit sig: " << (status&0x7f) << std::endl;std::cout << "wait child process done, exit code(ign): " << ((status>>8)&0xFF) << std::endl;}return 0;
}

管道的四种情况 和5种特征

【特征一】匿名管道:只能用来进行具有血缘关系的进程之间,进行通信,常用于父子进程之间通信

【情况1️⃣】如果管道内部是空的 并且 write fd没有关闭,读取条件不具备,读进程会被阻塞,解决方案,等待读取条件具备,也就是写入数据

【特征二】管道内部,自带进程之间同步的机制-->多执行流执行代码的时候,具有明显的顺序性

我们让子进程写慢一点,父进程持续去读

 

 

观察到的现象就是,每隔5秒打印一次,也就是父进程读取要和子进程一致,子进程慢了,父进程就要读慢点,子进程写一条,父进程读一条;在5s期间,父进程就在等待子进程写数据,也就是读进程被阻塞

【情况2️⃣】管道被写满,并且父进程不读且不关闭;管道被写满会被阻塞,如果要恢复正常就需要父进程读取数据(让写条件具备)

我们接下来让子进程疯狂写入,父进程不读,也就是给父进程休眠500s观察情况

 

现象就是子进程写到第65536就一直卡在哪里,我们父进程一直在,但是不读,这里我们也就可以计算出管道文件的大小65536/1024 = 64kb,也就是ubuntu下22.04管道大小是64kb

子进程写满之后就会被阻塞

【情况3️⃣】管道一直在读但是写端关闭了,读端read返回值会读到0,表示读到了文件结尾

我们让子进程写一条就关闭,父进程一直去读

 这样读端就会返回0,表示读到了文件结尾

【情况4️⃣】读端直接关闭,写端一直写入-->写端进程会被操作系统直接使用13号信号关掉,相当于进程出现了异常

 

【特征三】管道文件的生命周期是随进程的

【特征四】管道文件在通信的时候,是面向字节流的,写入的次数和读取的次数不是一一匹配的

如何理解特征四?

面向字节流:管道文件在通信时,数据是以字节为单位进行传输的。这意味着写入端可以一次写入多个字节,而读取端可以一次读取多个字节,或者可以分多次读取。

写入次数和读取次数不是一一匹配的:由于管道是半双工的,写入端和读取端的数据传输不是同步进行的。这意味着,写入端可能已经写入了多个字节,而读取端还没有开始读取,或者读取端已经读取了部分数据,而写入端还在继续写入。

数据传输是异步的:管道通信是异步的,这意味着写入端和读取端之间的数据传输不一定是连续的。写入端可以写入数据,然后继续执行其他操作,而读取端可以等待数据准备好后再读取。

数据缓冲:管道内部通常有一个缓冲区,用于存储写入端写入的数据。当读取端开始读取时,它会从缓冲区中读取数据。如果缓冲区满了,写入端可能会阻塞,直到有空间可用。如果缓冲区空了,读取端可能会阻塞,直到有数据可读。

管道通信的完整性:尽管写入次数和读取次数不是一一匹配的,但管道通信的完整性得到了保证。写入端写入的数据最终会被读取端读取,反之亦然。

【特征五】管道的通信模式,是一种特殊的半双工模式

在计算机网络和通信领域,半双工(Half-Duplex)和全双工(Full-Duplex)是描述通信设备或通道能否同时进行双向通信的术语。

半双工(Half-Duplex)

  • 半双工通信是指通信的双方可以交替地发送和接收数据,但不能同时进行。

  • 举个例子,对讲机就是半双工的,当一方正在说话时,另一方必须等待,不能同时说话。

  • 在半双工模式下,通信通道的带宽利用率较低,因为一方在发送数据时,另一方无法发送数据。

全双工(Full-Duplex)

  • 全双工通信是指通信的双方可以同时发送和接收数据,就像电话通话一样。

  • 在全双工模式下,通信通道的带宽利用率较高,因为双方可以同时进行通信,而不需要等待。

  • 全双工通常需要两个独立的通信通道,例如两个独立的物理线路,或者一个物理线路上的两个独立的逻辑通道。

总结来说,半双工通信需要轮流发送和接收数据,而全双工通信可以同时进行双向通信。全双工通信通常更高效,因为它允许多个设备或通道同时工作,而不需要等待。


http://www.mrgr.cn/news/54320.html

相关文章:

  • 每日新闻掌握【2024年10月22日 星期二】
  • 阿里字节技术管理岗位面试要求
  • string
  • 【MR开发】在Pico设备上接入MRTK3(一)——在Unity工程中导入MRTK3依赖
  • 阿里云验证码短信发送服务搭建(flask)
  • Spring Boot与JavaWeb协同:在线考试系统的实现“
  • NodeJS 使用百度翻译API
  • 顺序表算法题【不一样的解法!】
  • Lucas带你手撕机器学习——逻辑回归
  • OpenFeign的使用
  • AI学习指南深度学习篇-自编码器的变种
  • 论文精读:PRL 交变磁MnTe中的手性分裂磁振子
  • 场景化运营与定制开发链动 2+1 模式 S2B2C 商城小程序的融合
  • 【74LS48译码器】2022-1-2
  • 每天5分钟玩转C#/.NET之goto跳转语句
  • C++ | Leetcode C++题解之第494题目标和
  • TCP与UDP
  • Java最全面试题->Java基础面试题->JavaWeb面试题->Filter/Listener面试题
  • 基于PSO粒子群优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
  • Python 列表专题:列表可变性
  • 游戏投屏软件有哪些?分享这10款比较好用的!
  • Java ArrayList 深入解析
  • 思维导图怎么制作?这四款制作思维导图的软件值得推荐!
  • LLMS-Stable diffusion 报错 index out of bounds
  • 调整奇数偶数的顺序
  • 正则表达式 - 修饰符