无名管道和命名管道

管道的实质:内核中的一块缓冲区

 

管道是单工的,数据只能从一个方向流向另外一个方向(单向);想要双向通信时,需要建立两个管道。

先写进管道中的数据先被读出

 

无名管道:

  用于有血缘关系的进程

  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,如果写端没关闭,read会阻塞。

  当写一个读端已被关闭的管道(父子进程都将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来设置非阻塞;

posted @ 2023-03-02 23:04  踏浪而来的人  阅读(66)  评论(0编辑  收藏  举报