管道是没有名字的,因此它只能在有亲缘关系的进程间使用,给管道加上名字,我们称之为有名管道FIFO,当然FIFO与管道之间不止有没有名字的区别,还有其他区别下面会提到。与管道类似的是,FIFO是一个单向(半双工)数据流。不同于管道的是,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程之间访问同一个FIFO。FIFO是一种文件类型。stat结构成员st_mode的编码指明文件是否是FIFO类型,可以用S_ISFIFO宏对此进行测试。

FIFO由mkfifo函数创建,它已经隐含指定了O_CREAT|O_EXCL。也就是说,如果FIFO存在就返回一个EEXIST错误,如果不存在就创建一个新的FIFO。FIFO由open打开而不是mkfifo。因此要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,再检查它是否返回EEXIST错误,若返回该错误则改为调用open。

 

  1. #include <sys/types.h>

  2. #include <sys/stat.h>

  3. int mkfifo(const char *pathname, mode_t mode);

在创建出一个FIFO后,它必须是或者打开来读,或者打开来写,可由open或fopen打开函数。FIFO不能打开来既读又写,因为它是半双工的。

 

FIFO有下面两种用途:

1、FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。FIFO可被用于复制串行管道命令之间的输出流,于是也就不需要写数据到中间磁盘文件中。如:

  1. mkfifo fifo1

  2. prog3 < fifo1 &

  3. prog1 < infile | tee fifof1 | prog2

 

创建FIFO,然后在后台启动prog3,它从FIFO读数据。然后启动prog1,用tee将输出发送到FIFO和prog2。图解如下:

IPC:管道之FIFO

图 使用FIFO和tee将一个数据发送到两个进程

2、FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务器进程之间传递数据。对于客户-服务器程序,改用两个FIFO代替两个管道的代码如下:

mainfifo.c

 

  1. #include <unistd.h>

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <errno.h>

  5. #include <sys/stat.h>

  6. #include "fifo.h"

  7.  
  8. #define FIFO1 "/tmp/fifo.1"

  9. #define FIFO2 "/tmp/fifo.2"

  10. #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

  11.  
  12. extern void client(int, int);

  13. extern void server(int, int);

  14.  
  15. int

  16. main(int argc, char **argv)

  17. {

  18. int readfd, writefd;

  19. pid_t pid;

  20.  
  21. /*creat tow FIFOs; OK if they already exist*/

  22. if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno = EEXIST))

  23. printf("can't creat %s.\n", FIFO1);

  24. if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno = EEXIST)){

  25. unlink(FIFO1);

  26. printf("can't creat %s.\n", FIFO2);

  27. }

  28.  
  29. if((pid = fork()) < 0){

  30. printf("can't fork.\n");

  31. return -1;

  32. }else if(pid == 0){ /*child*/

  33. readfd = open(FIFO1, O_RDONLY, 0);

  34. writefd = open(FIFO2, O_WRONLY, 0);

  35.  
  36. server(readfd, writefd);

  37. exit(0);

  38. }

  39. /*parent*/

  40. writefd = open(FIFO1, O_WRONLY, 0);

  41. readfd = open(FIFO2, O_RDONLY, 0);

  42.  
  43. client(readfd, writefd);

  44.  
  45. waitpid(pid, NULL, 0); /*wait for child to terminate*/

  46.  
  47. close(readfd);

  48. close(writefd);

  49.  
  50. unlink(FIFO1);

  51. unlink(FIFO2);

  52. exit(0);

  53. }

client.c

 

 

 
  1. #include "fifo.h"

  2.  
  3. int

  4. client(int readfd, int writefd)

  5. {

  6. size_t len;

  7. ssize_t n;

  8. char buff[MAXLINE];

  9.  
  10. /*read pathname*/

  11. fgets(buff, MAXLINE, stdin);

  12. len = strlen(buff);

  13. if(buff[len-1] == '\n')

  14. len--;

  15.  
  16. /*write pathname to IPC channel*/

  17. write(writefd, buff, len);

  18.  
  19. /*read from IPC, write to standard output*/

  20. while((n = read(readfd, buff, MAXLINE)) > 0)

  21. write(STDOUT_FILENO, buff, n);

  22. }

server.c

 

 

 
  1. #include "fifo.h"

  2.  
  3. void

  4. server(int readfd, int writefd)

  5. {

  6. int fd;

  7. ssize_t n;

  8. char buff[MAXLINE+1];

  9.  
  10. /*read pathname frome IPC channel*/

  11. if((n = read(readfd, buff, MAXLINE)) == 0){

  12. printf("end-of-file while reading pathname.\n");

  13. return ;

  14. }

  15. buff[n] = '\0'; /*null terminate pathname*/

  16.  
  17. if((fd = open(buff, O_RDONLY)) < 0){

  18. /*error:must tell client*/

  19. snprintf(buff+n, sizeof(buff)-n, ":can't open, %s\n", strerror(errno));

  20. n = strlen(buff);

  21. write(writefd, buff, n);

  22. }else{

  23. /*open succeeded:copy file to IPC channel*/

  24. while((n = read(fd, buff, MAXLINE)) > 0)

  25. write(writefd, buff, n);

  26. close(fd);

  27. }

  28.  
  29. }

fifo.h

 

 

 
  1. #include <unistd.h>

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <string.h>

  5. #include <sys/types.h>

  6. #include <sys/wait.h>

  7. #include <fcntl.h>

  8. #include <errno.h>

  9.  

Makefile

 

 

 
  1. all:mainfifo

  2. mainfifo:mainfifo.c server.c client.c

  3. gcc server.c client.c mainfifo.c -o mainfifo

图解上面的程序如下:

 

 

IPC:管道之FIFO

图 使用两个FIFO的客户-服务器例子

上面的例子送虽然使用了FIFO,但仍然是有亲缘关系的进程间通信,下面给出无亲缘关系的客户和服务器的例子。

客户程序client_main.c如下:

 
  1. #include "fifo.h"

  2.  
  3. extern client(int, int);

  4.  
  5. int

  6. main(int argc, char **argv)

  7. {

  8. int readfd, writefd;

  9.  
  10. writefd = open(FIFO1, O_WRONLY, 0);

  11. readfd = open(FIFO2, O_RDONLY, 0);

  12.  
  13. client(readfd, writefd);

  14.  
  15. close(readfd);

  16. close(writefd);

  17.  
  18. unlink(FIFO1);

  19. unlink(FIFO2);

  20. exit(0);

  21. }

client.c

 
  1. #include "fifo.h"

  2.  
  3. int

  4. client(int readfd, int writefd)

  5. {

  6. size_t len;

  7. ssize_t n;

  8. char buff[MAXLINE];

  9.  
  10. /*read pathname*/

  11. fgets(buff, MAXLINE, stdin);

  12. len = strlen(buff);

  13. if(buff[len-1] == '\n')

  14. len--;

  15.  
  16. /*write pathname to IPC channel*/

  17. write(writefd, buff, len);

  18.  
  19. /*read from IPC, write to standard output*/

  20. while((n = read(readfd, buff, MAXLINE)) > 0)

  21. write(STDOUT_FILENO, buff, n);

  22. }

服务器程序server_main.c如下:

 
  1. #include "fifo.h"

  2. #include <sys/stat.h>

  3. #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

  4.  
  5. extern void server(int, int);

  6.  
  7. int

  8. main(int argc, char **argv)

  9. {

  10. int readfd, writefd;

  11.  
  12. /*creat two FIFOs:OK if they already exist*/

  13. if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)){

  14. printf("can't create %s.\n", FIFO1);

  15. }

  16. if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)){

  17. unlink(FIFO1);

  18. printf("can't create %s.\n", FIFO1);

  19. }

  20.  
  21. readfd = open(FIFO1, O_RDONLY, 0);

  22. writefd = open(FIFO2, O_WRONLY, 0);

  23.  
  24. server(readfd, writefd);

  25. exit(0);

  26. }

server.c

 
  1. #include "fifo.h"

  2.  
  3. void

  4. server(int readfd, int writefd)

  5. {

  6. int fd;

  7. ssize_t n;

  8. char buff[MAXLINE+1];

  9.  
  10. /*read pathname frome IPC channel*/

  11. if((n = read(readfd, buff, MAXLINE)) == 0){

  12. printf("end-of-file while reading pathname.\n");

  13. return ;

  14. }

  15. buff[n] = '\0'; /*null terminate pathname*/

  16.  
  17. if((fd = open(buff, O_RDONLY)) < 0){

  18. /*error:must tell client*/

  19. snprintf(buff+n, sizeof(buff)-n, ":can't open, %s\n", strerror(errno));

  20. n = strlen(buff);

  21. write(writefd, buff, n);

  22. }else{

  23. /*open succeeded:copy file to IPC channel*/

  24. while((n = read(fd, buff, MAXLINE)) > 0)

  25. write(writefd, buff, n);

  26. close(fd);

  27. }

  28.  
  29. }

fifo.h

 
  1. #include <unistd.h>

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <string.h>

  5. #include <sys/types.h>

  6. #include <sys/wait.h>

  7. #include <fcntl.h>

  8. #include <errno.h>

  9.  
  10. #define FIFO1 "/tmp/fifo.1"

  11. #define FIFO2 "/tmp/fifo.2"

  12. #define MAXLINE 4096


参考:《UNIX环境高级编程》、《UNIX网络编程 卷2:进程间通信》

 

相关文章: