Linux进程间通信二--有名管道FIFO

来源:互联网 发布:js 正则匹配数字 编辑:程序博客网 时间:2024/05/16 01:39

一、有名管道概念

  上一节对无名管道做了阐述,无名管道只能用在两个有亲缘关系的进程直接。而有名管道FIFO可以用在任意两进程之间,它依赖于文件系统,是一个存在的特殊文件。
  有名管道和普通文件一样具有磁盘存放路径。文件权限和其他属性。但是,有名管道和普通文件又有区别,有名管道并没有在磁盘正存放真正的信息,它存储的通信信息在内存中,两进程结束后自动丢失,拥有一个磁盘路径仅仅是一个接口,其目的是使进程间信息的编程更简单统一。通信的两个进程结束后,有名管道的文件路径本身仍然存在,这是和无名管道不一样的地方。有名管道的特性可以总结为一下几点:
  1.可以用于同一台PC下任意两进程通信;
  2.无法seek,要保持先入先出(FIFO)原则;
  3.有名管道通信是单项的;
  4.有名管道本身不会随通信双方的结束而消失,但有名管道中的内容会随着通信双方结束消失。

二、创建有名管道

  mkfifo()函数用来创建有名管道,其函数声明如下:

// come from /usr/include/sys/stat.h/*Create a new FIFO named PATH, with permission bits MODE*/int mkfifo(const char* path, __mode_t mode);

  第一个参数path为要创建的管道文件名;
  第二个参数mode为生成文件的模式。
  
  mkfifo()会根据参数建立特殊的有名管道文件,该文件必须不存在mkfifo()创建的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()函数打开FIFO文件是,O_NONBLOCK会有影响。

三、读写有名管道

  有名管道的实质和无名管道一样是一段内核管理的内存空间。但在通过write和read系统调用来执行读写操作前,需要调用open()函数来打开该文件。另外,操作无名无名管道的阻塞位置为open()位置,而不是有名管道的读写位置。

1.打开有名管道
  如果希望以的方式打开管道,则需要另一个进程以的方式打开管道,否则一直阻塞。反过来如果以的方式打开管道,则需要另一进程以的方式打开管道,否则一直阻塞。即,如果以某种方式打开有名管道,则系统将阻塞进程,直到有另一个进程(包括自己)以另一种方式打开该管道后才会继续执行。显然,一个进程以可读可写方式打开管道,当前进程充当了读和写两个身份,进程不会阻塞。

2.两进程已经完成打开管道操作,阻塞读操作按一下方式执行:
  a.如果管道中没有数据,读操作默认阻塞;
  b.如果管道中有数据,但小于期望读取数据量,读出所有数据返回;
  c.如果管道中有数据,但大于期望读取数据量,读出期望大小数据返回。

3.两进程已经完成打开管道操作,阻塞写操作按一下方式执行:
  a.如果管道中没有空间,写操作阻塞;
  b.如果管道中有空间,但小于期望写入数据量,写满空间后阻塞;
  c.如果管道中有空间,但大于期望写入数据量,写入数据后返回。

4.两进程已经完成打开管道操作,中途其中一个进程退出:
  a.未退出一端如果是写操作,将返回SIGPIPE信号;
  b.未退出一端如果是阻塞读操作,读操作将不再阻塞,直接返回0。

四、示例

  下面给出在父子进程间使用FIFO通信的示例,在非情缘关系的进程间原理是一样的:

#include <stdio.h>  #include <unistd.h>#include <signal.h>#include <sys/types.h>#include <fcntl.h>#define FIFO_PATH   "./fifo"void handler(int sig){    printf("sig=%d\n", sig);}int main()  {      signal(SIGPIPE, handler);    unlink(FIFO_PATH);          // 删除原来的管道    mkfifo(FIFO_PATH, 0644);    // 创建管道    pid_t pid = fork();    if (pid < 0) {        perror("fork error");        return -1;    }    if (0 == pid) {     // 子进程        int fd;        fd = open(FIFO_PATH, O_WRONLY);        write(fd, "hello world!", 12);        sleep(1);        close(fd);    } else {            // 父进程        int fd;        char buf[128] = {0};        fd = open(FIFO_PATH, O_RDONLY);        int ret = read(fd, buf, sizeof(buf));        printf("ret=%d buf=%s\n", ret, buf);        close(fd);    }    return 0;}

运行结果如下所示:
这里写图片描述