一.何谓管道?• 管道是Unix中最古⽼的进程间通信的形式。• 我们把从⼀个进程连接到另⼀个进程的⼀个数据流称为⼀个“管道”。二.匿名管道1.父子间通信的管道首先我们得记住匿名管道通常用来做父子进程间的通信。父进程fork()子进程是浅拷贝那么文件描述符表是一模一样的同一个fd对应的元素也会指向同一个文件(struct_flie)。父子进程用到同一个fd(文件描述符)就可以访问同一个struct_file进而让父子进程都看到同一个缓冲区上篇曾提到同一个缓冲区就是同一份资源这样父子进程就有了通信的条件。而这里的管道就是那个struct_file。负责管道通信的管道文件是不需要刷新到磁盘上的它不属于用户空间(虚拟地址空间)的范畴而是属于内核空间内存但它的结构是复用了虚拟地址空间里的struct file因而它也有文件描述符表有fd。父子进程是通过文件描述符(fd)去找的pipe文件不走虚拟地址空间那一套所以不发生写时拷贝。因而可以通过一个进程在管道上写一个进程在管道上读实现通信。管道文件会被配上单独的系统调用。该调用接口的名字是pipe使用该接口可以创建并打开一个管道(文件)。由上图可知pipe接口包含一个返回型参数pipefd可以获取父进程的两个指向管道的指针的fd而且管道文件的创建不需要文件路径所以这个文件也就没有名字因而它叫匿名管道既然管道没名字那如何保证父子进程打开的是同一个管道文件呢答案是子进程继承父进程的文件描述符表2.fd角度深度理解匿名管道原理3.实现demo可以记忆一下fds[0]有张嘴巴所以是读端。fds[1]有支笔所以是写端。这里实现和上图有点区别我们让父进程保留写子进程保留写。#includeiostream #includeunistd.h #includesys/types.h #includesys/wait.h #includecstdio #includecstring void ChildWrite(int wfd) { char buffer[1024]; int cnt 0; while(true) { //向buffer写入 snprintf(buffer,sizeof(buffer),I am a child,pid: %d,cnt: %d,getpid(),cnt); //向管道写数据 write(wfd,buffer,strlen(buffer)); sleep(1); } } void FatherRead(int rfd) { char buffer[1024]; while(true) { buffer[0] 0; ssize_t n read(rfd,buffer,sizeof(buffer) - 1);//系统调用read将数据从pipe读进buffer,读到的东西没有\0即便是字符串也没有因为向文件里写东西带\0会出乱码。 if(n 0)//n代表读到的字节数 { //添加\0 buffer[n] 0; std::cout child say: buffer std::endl; } } } int main() { //1.创建管道 int fds[2] {0};//fds[0]3-读端 fds[1]4-写端 int n pipe(fds); if(n 0) { std::cerr pipe error std::endl; return 1; } std::cout fds[0]: fds[0] std::endl; std::cout fds[1]: fds[1] std::endl; //2.创建子进程 pid_t id fork(); if(id 0) {//子进程 //3.关闭不需要的读写端形成通信信道 //规定最终父进程-读子进程-写 close(fds[0]); ChildWrite(fds[1]);//写入 close(fds[1]);//通信完就关掉全部fd指向的空间。 exit(0); } //3.关闭不需要的读写端形成通信信道 //规定最终父进程-读子进程-写 close(fds[1]); FatherRead(fds[0]);//读取 waitpid(id,nullptr,0); close(fds[0]); return 0 ; }注意这里waitpid()必须写到父进程读取之后因为我们写的代码里父子进程都是死循环如果waitpid()写到前面了父进程就会一直等待子进程导致死锁就一直卡在waitpid()处不会去读取了。4.管道的5种特性所谓同步机制就是读写时进程快的等慢的。5.管道通信的4种情况6.管道文件大小默认上限在现代 Linux 内核中Linux 2.6.11 之后管道的默认容量通常是64 KB。可以通过 fcntl(fd, F_SETPIPE_SZ, size) 动态修改该上限。