管道的实质:内核中的一块缓冲区
管道是单工的,数据只能从一个方向流向另外一个方向(单向);想要双向通信时,需要建立两个管道。
先写进管道中的数据先被读出。
无名管道:
用于有血缘关系的进程
int pipe(int fd[2])
返回:成功返回0;失败:-1
因为pipe函数在fork之前调用,所以只在内核中创建了一个管道,但是文件描述符却被复制了一份;
当父进程要写时,必须先关闭读端fd[0],在往写端fd[1]里面写,写完再关闭写端;子进程就要先关闭写端fd[1],在从读端fd[0]中读取,读完再关闭读端。=====》单工
//一个进程往管道里写,另一个进程从管道中读
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h> #include <sys/wait.h> char *cmd1[] = {"cat","/etc/passwd",NULL}; char *cmd2[] = {"grep","root",NULL}; int main(void) { pid_t pid; int fd[2]; if(pipe(fd) < 0){ printf("pipe failed!!\n "); exit(0); } for(int i = 0;i < 2;i++){ pid = fork(); if(pid < 0){ printf("fork failed!!!\n"); exit(0); }else if(pid == 0){//child process if(i == 0){ //first child 往管道里面写 //先关闭读端 close(fd[0]); //cat命令时输出到调准输出上的,所以要将输出重定向到管道的写端 if(dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO){//标准输出现在是指向fd[1] printf("dup2 failed !!!\n"); exit(0); } close(fd[1]); if(execvp(cmd1[0],cmd1) < 0){ printf("execvp error!!!\n"); exit(1); } break; } if(i == 1){//second child 从管道里面读 //写关闭写端 close(fd[1]); //grep命令是从标准输入读取,我们到从管道中读取,所以要将标准输入重定向到管道的读端 if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO){//标准输出现在是指向fd[1] printf("dup2 error !!!\n"); exit(0); } close(fd[0]); if(execvp(cmd2[0],cmd2) < 0){ perror("error:"); exit(1); } break; } }else{//parent process if(1 == i){ close(fd[0]); close(fd[1]); wait(NULL); wait(NULL); } } } return 0; }
管道是阻塞性的,若管道中没有数据,读取管道的那个进程将阻塞;
当一个进程不断往管道里面写数据并且没有进程从管道中读数据,只要管道没有装满就可以,一旦装满了就会报错。====》管道中的数据读了就没有了
不完整管道:
当读一个写端已被关闭的管道(父子进程都将fd[1]关闭了),当把管道中的数据读完后,read会返回0
当写一个读端已被关闭的管道(父子进程都将fd[0]关闭了),会产生一个SIGPIPE信号,写的那个进程可以忽略或捕捉这个信号。同时这个进程中的errorno被设置为EPIPE
标准库中管道的操作:
popen函数和system函数类似;
popen函数的内部实现原理:
命名管道:
int mkfifo(const char *pethname ,mode_t mode)
可以在两个没有任何关系的进程间通信;
命名管道本质也是内核中的一块缓存,但是他又在文件系统中以一个特俗的设备文件(管道文件)存在。=====>命名管道
在文件系统中只有一个索引块(i节点)存放文件的路径,没有数据块,所有数据是存放在内核中的;
命名管道读和写必须同时打开(即以读打开管道的进程和以写打开的进程都要运行),否则单独读或单独写会引起进程阻塞(即只有一个进程在运行就会阻塞)
命名管道和匿名管道的区别:
相同点:
都是阻塞性的读写;
都是用与socket的网络通信
阻塞不完整管道(有一端关闭):
当读一个写端已被关闭的管道(父子进程都将fd[1]关闭了),当把管道中的数据读完后,read会返回0
当写一个读端已被关闭的管道(父子进程都将fd[0]关闭了),会产生一个SIGPIPE信号,写的那个进程可以忽略或捕捉这个信号。同时这个进程中的errorno被设置为EPIPE
阻塞完整管道(两端都开启):
读时,要么阻塞,要么读到数据
写时,写满管道出错
非阻塞不完整管道(有一端关闭):
单纯读时直接报错;
当写一个读端已被关闭的管道(父子进程都将fd[0]关闭了),会产生一个SIGPIPE信号,写的那个进程可以忽略或捕捉这个信号。同时这个进程中的errorno被设置为EPIPE
非阻塞完整管道(两端都开启):
读时,直接报错;
写时,写满管道出错
不同点:
打开方式不一样;
pipe通过fcntl系统调用设置O_NONBLOCK来设置非阻塞读写;
FIFO可以通过fcntl或open来设置非阻塞;
标签:阻塞,无名,进程,管道,fd,关闭,命名,读端 From: https://www.cnblogs.com/zj-studyrecoding/p/17173963.html