美文网首页
Linux进程间通信 -- 匿名管道和FIFO

Linux进程间通信 -- 匿名管道和FIFO

作者: brownfeng | 来源:发表于2020-02-03 16:55 被阅读0次

    Linux进程间通信 -- 匿名管道和FIFO

    匿名管道

    管道包括三种:

    1. 匿名管道pipe. 特点:
      1. 一是单工(单项传输)
      2. 二是只能在父子或者兄弟进程使用
    2. 流管道s_pipe. 特点:
      1. 半双工(可以双向传输,但不能同时读写)
      2. 只能在父子或兄弟进程间使用
    3. 命名管道FIFO. 特点:
      1. 半双工
      2. 不相关进程之间也可进行通讯

    管道特点是由Linux内核开辟一段缓冲区, 管道的两端分别连接两个进程, 管道有如下特点:

    1. 管道一端连接的进程向管道中进行输入,管道另一端的进程从管道中读取数据. 一般一个管道被设计成环形数据结构,可以被循环利用.
    2. 如果管道中没有信息的话,从管道中读的进程会进等待,直到另一端的进程写入数据到管道中.
    3. 当管道被写满时,尝试写入的进程会等待,直到另外一端读出信息.
    4. 当两个进程都终结的时, 管道也会自动消失.

    具体api如下:

    #include <unistd.h>
    int pipe(int filedes[2]);
    

    如果创建成功,会打开两个文件描述符:

    1. filedes[0]用于读出数据,读取时必须关闭写入端,即close(filedes[1]);
    2. filedes[1]用于写入数据,写入时必须关闭读取端,即close(filedes[0])

    实现ps aux | grep bash:

    #include <stdio.h>
    #include <unistd.h>
    int main() {
      int fd[2];
      pipe(fd);
      pid_t pid = fork();
      if (pid == 0) {
        // son -- ps
        // 1. redirect
        close(fd[0]);               // close read
        dup2(fd[1], STDOUT_FILENO); // stdout -> fd[1]
        execlp("ps", "ps", "aux", NULL);
      } else if (pid > 0) {
        close(fd[1]);
        // parent
        // redirect: stdin -> fd[0]
        dup2(fd[0], STDIN_FILENO);
        execlp("grep", "grep", "bash", NULL);
      }
      return 0;
    }
    

    对读管道:

    1. 写端全部关闭 -- read读到0,相当于读到文件末尾
    2. 写端没有全部关闭
      1. 有数据 -- read读到数据
      2. 没有数据 -- read堵塞, 使用fcntl函数可以改成非阻塞

    对写管道:

    1. 读端完全关闭 -- ? 产生一个信号SIGPIPE,程序异常终止
    2. 读端未全部关闭
      1. 管道已满 -- write阻塞
      2. 管道未满 -- write正常写入

    可以使用ulimit -a, 可以打印出当前进程的各种限制, 其中pipe size就是系统创建的匿名管道的大小:

    查看限制情况 ulimit -a
    
    可以看到如下信息
    
    core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 1024
    max locked memory       (kbytes, -l) 32
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 1024
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    stack size              (kbytes, -s) 10240
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 4096
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited
    

    有名管道FIFO

    FIFO是有名管道, 可以实现无血缘关系的进程间通信.

    1. 我们可以用Linux命令mkfifo创建一个.实际创建的是一个伪文件.
    2. 也可以用函数int mkfifo(...)创建

    Linux内核会针对FIFO文件开辟一个缓冲区,操作FIFO文件,实现进程间通信, 其实可以认为FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件).

    linux系统API创建FIFO, 和创建文件差不多:

    #include <sys/types.h>
    #include <sys/stat.h>
    
    int mkfifo(const char *filename, mode_t mode);
    
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    
    int main()  
    {  
        int res = mkfifo("/tmp/my_fifo", 0777);  
        if (res == 0)  
        {  
            printf("FIFO created/n");  
        }  
         exit(EXIT_SUCCESS);  
    }
    

    从FIFO文件中读:

    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    int main(int argc, char *argv[]) {
      if (argc != 2) {
        printf("./a.out fifoname\n");
        return -1;
      }
      // open fifo file
      int fd = open(argv[1], O_RDONLY);
      char buf[256];
      int ret = 0;
      while (1) {
        ret = read(fd, buf, sizeof(buf));
        if (ret > 0) {
          printf("read:%s\n", buf);
        }
        sleep(1);
      }
    
      close(fd);
      return 0;
    }
    

    向FIFO中写:

    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    int main(int argc, char *argv[]) {
      if (argc != 2) {
        printf("./a.out fifoname\n");
        return -1;
      }
      // open fifo file
      int fd = open(argv[1], O_WRONLY);
      char buf[256];
      int num = 1;
      while (1) {
        memset(buf, 0x00, sizeof(buf));
        sprintf(buf, "xiaoming%04d", num++);
        write(fd, buf, strlen(buf));
        sleep(1);
      }
    
      close(fd);
      return 0;
    }
    

    注意: FIFO的open时,read端会阻塞等待write端open, wirte端同理,也会阻塞等待另一端打开.

    相关文章

      网友评论

          本文标题:Linux进程间通信 -- 匿名管道和FIFO

          本文链接:https://www.haomeiwen.com/subject/zrzhxhtx.html