FIFO 也称为“命名管道”。它在文件系统中有一个相应的文件,称为管道文件。FIFO文件可以通过mkfifo()函数创建。在FIFO文件创建之后,任何一个具有适当权限的进程都可以打开它,从文件描述符中读取或写入数据。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
注意:在程序结束后,系统不会自动删除FIFO文件。若要删除,可以用unlink()函数删除
打开规则
如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。
注意:写操作打开FIFO时,如果没有进程为读而打开FIFO,非阻塞状态将返回INSIO错误;阻塞的话将阻塞到有进程为读打开FIFO.
读操作打开FIFO时,如果没有进程为写打开FIFO,非阻塞状态返回成功;阻塞的话将阻塞到有进程为写而打开FIFO.
读写规则
约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。
- 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
- 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其它进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。
- 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。
- 如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。
注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。
向FIFO中写入数据:
约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。
对于设置了阻塞标志的写操作:
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
对于没有设置阻塞标志的写操作:
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
/*define a function pid_printf*/
void pid_printf(char *format, ...)
{
va_list ap;
va_start(ap, format);
printf("[%d]%d:", getpid(),__LINE__);
vprintf(format, ap);
}
#define FIFO_PATH "/tmp/fifo_test"
char end = 'a';
void server()
{
#if 1
char buf[20]="hellohehelloworld!";
int fd;
if(mkfifo(FIFO_PATH, 0600))
{
perror("mkfifo");
}
pid_printf("The server is writing...\n");
fd = open(FIFO_PATH, O_WRONLY);
pid_printf("fd=[%d] errno=[%d]\n", fd, errno);
int i;
for(i=0; i < 20; ++i)
{
sleep(2);
buf[18] = end++;
if(end == 'z'+1)
end = 'a';
write(fd, buf, sizeof(buf));
pid_printf("write=[%s]\n", buf);
}
close(fd);
sleep(9);
unlink(FIFO_PATH);
#endif
#if 0
char buf[256]={0};
int fd;
int count, status;
if(mkfifo(FIFO_PATH, 0660))
{
perror("mkfifo");
}
fd=open(FIFO_PATH, O_RDONLY);
while(1)
{
count =0;
while(count< sizeof(buf)-1)
{
status=read(fd, buf+count, 1);
if(status<1)
{
pid_printf("read eof,server exit\n");
goto server_exit;
}
if(buf[count]=='\n')
{
buf[count] = 0;
break;
}
count++;
}
pid_printf("receive [%s]\n", buf);
}
server_exit:
close(fd);
unlink(FIFO_PATH);
#endif
}
void client()
{
#if 1
int fd;
char buf[20]="0";
fd=open(FIFO_PATH, O_RDONLY);
pid_printf("client is reading...\n");
int i;
for(i=0; i<20; ++i)
{
sleep(3);
read(fd, buf, sizeof(buf));
pid_printf("read=[%s]\n", buf);
}
close(fd);
#endif
#if 0
int fd;
char buf[256];
fd=open(FIFO_PATH, O_WRONLY);
pid_printf("the client is ready...\n");
while(fgets(buf, sizeof(buf), stdin)!=NULL)
{
write(fd, buf, strlen(buf));
}
pid_printf("client exit...\n");
close(fd);
#endif
}
int main(int argc, char *argv[])
{
if(argc < 2)
server();
else
client();
return 0;
}
注意:程序有参数时运行客户端,否则运行服务端。