Linux进程间通信方式——管道

管道

什么是管道

管道是UNIX系统最古老IPC(进程间通信)方式,所有UNIX系统都提供这种通信机制。

管道就是从一个进程连接到另一个进程的一个数据流

局限性

  1. 为了可移植性,管道都采用历史沿用的半双工(数据只能单向流动)通信
  2. 管道只能在具有公共祖先的两个进程间使用

匿名管道

pipe

创建一个匿名管道

由参数返回两个文件描述符:fd[0] 为读端(从管道读)打开,fd[1]为写端(从管道写)打开(读写端与默认文件描述符相反)

管道示意图

Linux进程间通信方式——管道_第1张图片

由于单个进程中的管道几乎没有任何用处,所以通常进程会先调用pipe,接着调用fork,创建父进程到子进程的IPC管道,如图:

Linux进程间通信方式——管道_第2张图片

fork之后做什么取决于数据流方向(管道永远流向读端(关闭 fd[1] 的那端)):

  • 父进程关闭读端 fd[0] 子进程关闭写端 fd[1] 管道流向从父进程到子进程
  • 父进程关闭写端 fd[1] 子进程关闭读端 fd[0] 管道流向从子进程到父进程
  • 读0写1,永远流向读,读关1,写关0。

父进程通过管道读取子进程buf,并输出到stdout:

#include 
#include 
#include 
#include 

int main()
{
    int fds[2];
    char buf[20];

    if(pipe(fds) == -1){
        perror("pipe error");
        exit(1);
    }
    
    pid_t pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }
    //child
    if(pid == 0){
      //close read
      close(fds[0]);
      write(fds[1], "hello world", 20);
      //close write
      close(fds[1]);
      exit(1);
    }
    
    //close write
    close(fds[1]);
    read(fds[0], buf, 20);
    close(fds[0]);
   
    printf("buf = %s\n", buf);
    return 0;
}

管道读写规则(man 7 pipe)

1. 非阻塞 O_NONBLOCK

2. 管道无数据可读时

  • O_NONBLOCK disable(关闭):read调用阻塞,直到有数据来为止
  • O_NONBLOCK enable(开启):read调用返回-1,errno值为EAGAIN

3. 管道满时

  • O_NONBLOCK disable(关闭): write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable(开启):调用返回-1,errno值为EAGAIN

4. 原子性

写入的数据量 小于等于 PIPE_BUF(4096字节)时,Linux保证写入操作的原子性

写入的数据量 大于 PIPE_BUF(4096字节)时,Linux将不保证写入操作的原子性

5. 管道的一端被关闭

  • 读 read 一个写端 fd[1] 关闭的管道时,所有数据都被读取走后,read 返回0,表示文件结束
  • 写 write 一个读端 fd[0] 已被关闭的管道时,产生信号SIGPIPE,如果忽略该信号或捕捉该信号从处理程序返回,则 write 返回-1,errno设置为EPIPE

协同进程

UNIX系统过滤程序从标准输入读取数据,向标准输出写数据,几个过滤程序在shell管道中线性连接,当一个过滤程序既产生某个过滤程序的输入又读取该过滤程序的输出时,就称它为协同进程

Linux进程间通信方式——管道_第3张图片

分别从文件系统和文件描述符理解管道:

Linux进程间通信方式——管道_第4张图片

命名管道(FIFO)

匿名管道只能用于两个具有亲缘关系的进程间通信,但是FIFO可以在不相关的进程之间交换数据

FIFO本质是一种特殊的文件

作用:

    Linux进程间通信方式——管道_第5张图片 Linux进程间通信方式——管道_第6张图片

  1. shell命令使用FIFO将数据从一条管道传送到另一条管道无需创建中间临时文件
  2. 客户端-服务器中用作汇聚点在客户进程和服务进程间传递数据

mkfifo

创建命名管道

mode 中的参数与 open 中的 mode 参数相同

Linux进程间通信方式——管道_第7张图片

0644用户可读可写其他人可读

Server.c

#include 
#include 
#include 
#include 
#include 
#include 

#define ERROR_EXIT(m) do {perror(m); exit(EXIT_FAILURE);} while(0)

int main()
{
    umask(0);
    if(mkfifo("mypipe", 0644) < 0){
        ERROR_EXIT("mkfifo");
    }
    int rfd = open("mypipe", O_RDONLY);
    if(rfd < 0){
        ERROR_EXIT("open");
    }

    char buf[1024];
    while(1){
        buf[0] = 0;
        printf("Please wait...\n");
        ssize_t s = read(rfd, buf, sizeof(buf)-1);
        if(s > 0){
            buf[s-1] = 0;
            printf("client say# %s\n", buf);
        } else if(s == 0){
            printf("client quit, exit now!\n");
            exit(EXIT_SUCCESS);
        } else{
            ERROR_EXIT("read");
        }
    }

    close(rfd);
    return 0;
}

Client.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ERROR_EXIT(m) do {perror(m); exit(EXIT_FAILURE);} while(0)

int main()
{
    int wfd = open("mypipe", O_WRONLY);
    if(wfd < 0){
        ERROR_EXIT("open");
    }

    char buf[1024];
    while(1){
        buf[0] = 0;
        printf("Plese Enter# ");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s > 0){
            buf[s] = 0;
            write(wfd, buf, strlen(buf));
        } else if(s <= 0){
            ERROR_EXIT("read");
        }
    }

    close(wfd);
    return 0;
}

Linux进程间通信方式——管道_第8张图片

Linux进程间通信方式——管道_第9张图片

你可能感兴趣的:(Linux,进程间通信,管道,信号量,共享内存,消息队列)