FIFO 指代先进先出,unix 中 FIFO 与管道类似,是一个单向(半双工)的数据流;区别在于每个FIFO都有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO,而管道没有名字,只能用于有一个共同祖先进程的各个进程之间。所有FIFO有时也称为有名管道。
FIFO由mkfifo函数创建,函数原型如下:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char* pathname, mode_t mode);
其中pathname是一个普通的Unix路径名,它是该FIFO的名字。mode参数指定文件权限位,类似于open的第二个参数
mkfifo 已隐含指定 O_CREAT|O_EXCL。即它要么创建一个新的 FIFO,要么返回一个 EEXIST 错误。
要打开一个已存在的 FIFO 或创建一个新的 FIFO ,应先调用 mkfifo,检查它是否返回EEXIST错误(说明已经存在,这样可以直接打开),若返回 EEXIST 则改为调用open。
接下来使用两个 FIFO 代替两个pipe来重写《初识管道》中的例子,实现客户-服务器程序,其中的server与client函数不变,只修改main函数中的代码。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/fcntl.h> #define MAXLINE 4096 #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) void client(int readfd, int writefd) { size_t len; ssize_t n; char buf[MAXLINE]; fgets(buf, MAXLINE, stdin); len = strlen(buf); if(buf[len-1] == '\n') len--; write(writefd, buf, len); while((n = read(readfd, buf, MAXLINE)) > 0) write(STDOUT_FILENO, buf, n); } void server(int readfd, int writefd) { int fd; ssize_t n; char buf[MAXLINE + 1]; if((n = read(readfd, buf, MAXLINE)) == 0) { printf("read end of file!"); exit(0); } buf[n] = '\0'; if((fd = open(buf, 'r'))< 0) { snprintf(buf+n, sizeof(buf)-n,": can't open, %s\n", strerror(errno)); n = strlen(buf); write(writefd, buf, n); } else { while((n = read(fd, buf, MAXLINE)) > 0) write(writefd, buf, n); close(fd); } } int main(int argc, char **argv) { int readfd, writefd; pid_t childpid; if((mkfifo(FIFO1, FILE_MODE) < 0) && errno != EEXIST) { printf("can't create %s\n", FIFO1); exit(1); } if((mkfifo(FIFO2, FILE_MODE) < 0) && errno != EEXIST) { unlink(FIFO1); //删除创建的FIFO并释放 printf("can't create %s", FIFO2); exit(2); } if((childpid = fork()) == 0) { //子进程 readfd = open(FIFO1, O_RDONLY, 0); writefd = open(FIFO2, O_WRONLY, 0); server(readfd, writefd); exit(0); } writefd = open(FIFO1, O_WRONLY, 0); readfd = open(FIFO2, O_RDONLY, 0); client(readfd, writefd); waitpid(childpid, NULL, 0); close(readfd); close(writefd); unlink(FIFO1); unlink(FIFO2); exit(0); }
编译运行:
wu@ubuntu:~/myapue$ ./usefifo
fifotest
hello wuyudong
FIFO test
open的顺序不对的话,程序有可能不能执行,如果互换父进程的open调用顺序,这时没有进程打开FIFO来写,于是父子进程打开FIFO来读都会产生阻塞,这种现象称为死锁
上面的程序客户和服务器仍然有亲缘关系,下面重写代码实现无亲缘关系
server_fifo.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/fcntl.h> #define MAXLINE 4096 #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) void server(int readfd, int writefd) { int fd; ssize_t n; char buf[MAXLINE + 1]; if((n = read(readfd, buf, MAXLINE)) == 0) { printf("read end of file!"); exit(0); } buf[n] = '\0'; if((fd = open(buf, 'r'))< 0) { snprintf(buf+n, sizeof(buf)-n,": can't open, %s\n", strerror(errno)); n = strlen(buf); write(writefd, buf, n); } else { while((n = read(fd, buf, MAXLINE)) > 0) write(writefd, buf, n); close(fd); } } int main(int argc, char **argv) { int readfd, writefd; if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)) { printf("can't create %s", FIFO1); exit(1); } if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST)) { unlink(FIFO1); printf("can't create %s", FIFO2); exit(1); } readfd = open(FIFO1, O_RDONLY, 0); writefd = open(FIFO2, O_WRONLY, 0); server(readfd, writefd); exit(0); }
client_fifo.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/fcntl.h> #define MAXLINE 4096 #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) void client(int readfd, int writefd) { size_t len; ssize_t n; char buf[MAXLINE]; fgets(buf, MAXLINE, stdin); len = strlen(buf); if(buf[len-1] == '\n') len--; write(writefd, buf, len); while((n = read(readfd, buf, MAXLINE)) > 0) write(STDOUT_FILENO, buf, n); } int main(int argc, char **argv) { int readfd, writefd; writefd = open(FIFO1, O_WRONLY, 0); readfd = open(FIFO2, O_RDONLY, 0); client(readfd, writefd); close(readfd); close(writefd); unlink(FIFO1); unlink(FIFO2); exit(0); }
首先在后台运行服务器,接着运行客户端:
wu@ubuntu:~/myapue$ gcc server_fifo.c -o server_fifo
wu@ubuntu:~/myapue$ gcc client_fifo.c -o client_fifo
wu@ubuntu:~/myapue$ ./server_fifo &
[1] 10471
wu@ubuntu:~/myapue$ ./client_fifo
fifotest
hello wuyudong
FIFO test
Comments