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