fifo简介
FIFO与管道类似,是一个单向数据流,但由于FIFO与某个路径名关联,允许无亲缘关系的进程访问同一个FIFO进行通信。
创建FIFO
FIFO可由mkfifo函数或命令创建,函数原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
说明:
- mkfifo函数隐含指定O_CREAT | O_EXCL,即要么创建一个新的FIFO,要么返回EEXIST错误。
- 如要打开一个已存在的FIFO或创建一个新的FIFO,应先调mkfifo,检查它是否返回EEXIST,如果是,再调用open。
- 由于FIFO是半双工的,用open/fopen打开时,要么打开读,要么打开写。
- 对管道或FIFO的write总是往末尾写数据,read总是从开头读数据。如对管道或FIFO调用lseek,将返回ESPIPE错误。
- 以读方式打开某FIFO将阻塞,直到有进程以写方式打开该FIFO;同样以写方式打开某FIFO将阻塞,直到有进程以读方式打开该FIFO。
利用FIFO通信示例
回射服务端
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#define FIFO1 "/tmp/fifo1"
#define FIFO2 "/tmp/fifo2"
int main() {
int rfd, wfd, len;
char buf[256];
if (mkfifo(FIFO1, 0644) < 0 && errno != EEXIST) {
fprintf(stderr, "create fifo %s failed", FIFO1);
return 1;
}
if (mkfifo(FIFO2, 0644) < 0 && errno != EEXIST) {
fprintf(stderr, "create fifo %s failed", FIFO2);
unlink(FIFO1);
return 1;
}
rfd = open(FIFO1, O_RDONLY, 0);
wfd = open(FIFO2, O_WRONLY, 0);
while ((len = read(rfd, buf, sizeof(buf))) > 0) {
write(STDOUT_FILENO, buf, len);
write(wfd, buf, len);
}
close(rfd);
close(wfd);
unlink(FIFO1);
unlink(FIFO2);
return 0;
}
回射客户端
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#define FIFO1 "/tmp/fifo1"
#define FIFO2 "/tmp/fifo2"
int main() {
int rfd, wfd, len;
char buf[256];
wfd = open(FIFO1, O_WRONLY);
rfd = open(FIFO2, O_RDONLY);
while (fgets(buf, sizeof(buf), stdin)) {
write(wfd, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
len = read(rfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, len);
}
close(wfd);
close(rfd);
return 0;
}
注意点:
- 创建FIFO时权限0644不能写成644,相当于(O_IRUSR | O_IWUSR | O_IRGRP | O_IROTH)。
- 注意服务器和客户端打开两个FIFO的顺序,避免死锁。
非阻塞IO设置
将描述符设置成非阻塞有两种方式:
- 在调用open时指定O_NONBLOCK标志。
- 通过fctnl调用。
int flag = fcntl(fd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(fd, SETFL, flag);
第二种方式更为通用,管道只能以该方式设置成非阻塞。
管道IO特点
关于管道和FIFO的读写,有以下特点:
- 如果请求读出的数据量多于管道或FIFO当前可用数据量,那么只返回可用的数据,因此要处理read返回值小于请求数的情况。
- 如果请求写入的字节数不超过PIPE_BUF,那么write操作是原子的,反之不能保证是原子的。
- O_NONBLOCK对write操作的原子性没有影响,原子性只取决于请求写入数是否小于等于PIPE_BUF。
- 若管道或FIFO设置成O_NONBLOCK,write的返回值取决于待写的字节数和管道或FIFO当前可用空间大小。
- 如果待写字节数不大于PIPE_BUF,而管道或FIFO有足够空间,则所有数据全部写入;反之返回EAGAIN错误。
- 如果待写字节数大于PIPE_BUF,而管道或FIFO至少有1字节空间,则将其写满,同时返回写入字节数;否则返回EAGAIN错误。
- 向一个没有为读打开的管道或FIFO写入,将产生SIGPIPE信号。如不处理该信号,进程将终止;如忽略或捕获了该信号,write返回EPIPE。