FIFO
参考:http://blog.csdn.net/haomcu/article/details/46647843
FIFO 表示的是命名管道,可以在两个不相关进程之间进行通信(注:未命名管道是pipe)。
FIFO 也表示一种文件类型,可以通过S_ISFIFO(stat结构的st_mode成员的编码)宏来测试文件是否是FIFO类型.
创建FIFO:
#include<sys/stat.h> //mode参数和open函数中的mode参数相同
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char * path, mode_t mode);
mkfifoat和mkfifo类似,不过mkfifoat可以在fd表的文件目录下创建一个FIFO,mkfifoat有三种情况:
1. 如果path是绝对路径,则忽略fd,这时和mkfifo相同。
2. 如果path是相对路径,则fd是一个打开目录的有效文件描述符,。
3. 如果path是相对路径,并且fd有AT_FDCWD标志,则pathname以当前工作目录开始(这时与mkfifo类似)。
注:
建了一个FIFO后,要用open来打开它,实际上,一般的文件I/O函数(write,read,close,unlink等)都需要用FIFO。
在POSIX.1的XSI扩展中应用程序可以用接口mknod,mknodat函数来创建FIFO.
当open一个FIFO时,非阻塞标志(O_NONBLOCK)会产生下面的影响:
参考: http://blog.csdn.net/erlian_beijing/article/details/46698401
特点一:不指定O_NONBLOCK(即open没有位或O_NONBLOCK)
1、open以只读方式打开FIFO时,要阻塞到某个进程为写而打开此FIFO : FIFO读 阻塞 直到 fifo写打开
2、open以只写方式打开FIFO时,要阻塞到某个进程为读而打开此FIFO : FIFO写 阻塞 直到 fifo读打开
3、open以只读、只写方式打开FIFO时会阻塞,调用read函数从FIFO里读数据时read也会阻塞。(此时读写阻塞)
4、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞。(写缓冲区慢)
5、通信过程中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又重新运行,则调用read函数从FIFO里读数据时又恢复阻塞。
6、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到SIGPIPE信号)退出。(fifo read 退出导致fifo write 收到SIGPIPE退出)
特点二:指定O_NONBLOCK(即open位或O_NONBLOCK)
1、先以只读方式打开:如果没有进程已经为写而打开一个FIFO, 只读open成功,并且open不阻塞。(fifo 读,且fifo 写没打开,read 成功返回)
2、先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open将出错返回-1。(fifo 写 则会返回-1)
3、read、write读写命名管道中读数据时不阻塞。
4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到SIGPIPE信号)退出。
5、write一个没有无进程为读而打开的FIFO,将产生SIGPIPE信号。
参考: http://blog.csdn.net/yefengzhichen/article/details/50262465s
https://www.cnblogs.com/benxintuzi/p/4781779.html
http://blog.csdn.net/yefengzhichen/article/details/50262465
Fifo 的用途:
1) shell命令使用FIFO将数据从一条管道传送到另一条管道,无需创建中间临时文件。
2) 客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程之间传递数据。
实例:考虑这样一个过程,他需要对一个输入文件进行两次处理,示意图如下:
使用FIFO和tee将一个流发送到两个不同的进程: 我们可以使用FIFO和tee命令如下处理:
mkfifo fifo1 :创建FIFO
prog3 < fifo1 & :后台运行prog3,并将标准输入重定向为fifo1
prog1 < (输入文件) | tee fifo1 | prog2 :prog1的标准输入重定向到infile,标准输出通过管道连接tee的标准输入,tee程序的参数是fifo1(程序内部可以向
FIFO写),然后tee的标准输出通过管道连接到prog2的标准输入。
执行流程如下:
2、使用FIFO进行client 进程-server 进程通信
首先看下图,well-known FIFO是server和所有client都知道FIFO的路径名
上图如果server要响应client该怎么办呢? 可以: client发请求的时候顺带发送自己的pid,server根据client pid来创建一个FIFO。
如下图的形式: 专门用来和该client process通信(例如命名为/tmp/serv1.XXXX XXXX为client pid),
这样问题似乎得到了解决,但考虑以下情况:
· client请求完后崩溃了怎么办?那么client-specific FIFO的读端被关闭,所以server写的时候就会返回SIGPIPE信号,server必须处理这种信号。
那么这样看来,这种方法也不尽合理。
还有最后几点要说明的是,
(1)FIFO只适用于单机系统;如果在NFS上(网络文件系统,“容许不同客户端及服务端通过一组RPC分享相同的文件系统”),肯定是不行的;
(2)上述的简单服务器是“迭代服务器(iterative server)”,也就是server在完全处理完一个client请求后,在处理下一个;另外一种设计是“并发服务器(concurrent server)”,UNIX下常见的one-child-per-client server就是,即为每个客户分配一个进程(或线程);
(3)迭代服务器存在的一个问题是DoS(拒绝服务型攻击),如上图,client只发送请求但并不读为自己创建的FIFO,这样会使server阻塞;解决办法是:设阻塞时钟,或改为并发服务器,虽然并发服务器面临大量DoS时也许会因为fork达到上限而无法继续fork,但总比迭代服务器好些。
http://blog.csdn.net/to_be_it_1/article/details/28384117
例: client 与 server 通信程序
Server 负责创建FIFO1, FIFO2管道, client 从标准输入中读取文件名 传给server, server 打开文件名,
然后将文件内容写到管道FIFO2,client 读取后打印到终端。
Client_main.c:
#include "fifo.h"
extern client(int, int);
int main(int argc, char **argv)
{
int readfd, writefd;
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
if(readfd < 0)
{
printf("open fail : %d\nerror message: %s\n", errno, strerror(errno));
}
client(readfd, writefd);
close(readfd);
close(writefd);
unlink(FIFO1);
unlink(FIFO2);
return 0;
}
Client.c:
int client(int readfd, int writefd)
{
size_t len;
ssize_t n;
char buff[MAXLINE];
/*read pathname*/
fgets(buff, MAXLINE, stdin);
len = strlen(buff);
if(buff[len-1] == '\n')
len--;
/*write pathname to IPC channel*/
write(writefd, buff, len);
printf("client send: %s\0", buff);
/*read from IPC, write to standard output*/
while((n = read(readfd, buff, MAXLINE)) > 0)
{
printf("client recv: %s\n", buff);
write(STDOUT_FILENO, buff, n);
}
printf("client exit !\n");
return 0;
}
Fifo.h:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
#define MAXLINE 4096
Server_main.c:
#include "fifo.h"
#include <sys/stat.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
extern void server(int, int);
int main(int argc, char **argv)
{
int readfd, writefd;
/*creat two FIFOs:OK if they already exist*/
if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST))
{
printf("can't create %s.\n", FIFO1);
}
if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST))
{
unlink(FIFO1);
printf("can't create %s.\n", FIFO1);
}
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
server(readfd, writefd);
exit(0);
//return 0;
}
Server.c:
#include "fifo.h"
void server(int readfd, int writefd)
{
int fd;
ssize_t n;
char buff[MAXLINE+1];
/*read pathname frome IPC channel*/
if((n = read(readfd, buff, MAXLINE)) == 0){
printf("end-of-file while reading pathname.\n");
return ;
}
buff[n] = '\0'; /*null terminate pathname*/
printf("server recv: %s\n", buff);
if((fd = open(buff, O_RDONLY)) < 0)
{
/*error:must tell client*/
snprintf(buff+n, sizeof(buff)-n, ":can't open, %s\n", strerror(errno));
n = strlen(buff);
write(writefd, buff, n);
}else
{
/*open succeeded:copy file to IPC channel*/
while((n = read(fd, buff, MAXLINE)) > 0)
{
if( write(writefd, buff, n) < 0)
{
printf("send error : %d\nerror message: %s\n", errno, strerror(errno));
}
printf("server write---%d--: %s\n", n, buff);
}
}
close(fd);
}
执行:
Client:
Server: