简介
FIFO又被称为命名管道,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,一般的文件I/O函数都可以用于FIFO。
打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:
- 未指定O_NONBLOCK
只读open()要阻塞到某个其他进程为写而打开此FIFO。类似的,只写open()要阻塞到某个其他进程为读而打开它。
- 指定O_NONBLOCK
只读open()立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写open()将出错返回-1,其errno是ENXIO。
基于O_NONBLOCK对FIFO操作的影响,一种比较好的操作FIFO的方式是以非阻塞的方式打开文件,然后以阻塞的方式读写文件或使用select。
一个给定的FIFO有多个写进程是很常见的。这意味着如果不希望多个进程写的数据互相穿插,则需考虑原子操作,常量PIPE_BUF说明了可被原子地写到FIFO的最大数据量。
FIFO有以下两种用途:
- FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此不需要创建中间临时文件。
- FIFO用于客户进程-服务器进程的应用程序中,以在客户进程和服务器进程中传递数据。
示例程序
阻塞版本
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#define RECV_BUF_SIZE 1024
#define SEND_BUF_SIZE 1024
#define CONTENT_LEN 128
#define ARGUMENT_LEN 128
#define DATA_SEPARATOR ':'
#define SERVER_FIFO_PATH "/tmp/fifo_server"
int server_write_fd, server_read_fd, client_write_fd;
char send_buf[SEND_BUF_SIZE];
char content[CONTENT_LEN];
char client_fifo_path[ARGUMENT_LEN];
char recv_buf[RECV_BUF_SIZE];
char send_buf[SEND_BUF_SIZE];
static int fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP;
/*
* create server fifo
* success : 0 failed : -1
*/
int fifo_init(void)
{
int ret;
struct stat filestat;
ret = stat(SERVER_FIFO_PATH, &filestat);
if(ret == 0){
if (unlink(SERVER_FIFO_PATH) < 0){
perror("Cannot delete old MI fifo");
return -1;
}
}else if(ret < 0 && errno != ENOENT){
perror("MI FIFO stat failed");
return -1;
}
if((mkfifo(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Create client fifo error");
return -1;
}
if((chmod(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Chmod client fifo error");
goto error;
}
return 0;
error:
remove(SERVER_FIFO_PATH);
return -1;
}
/*
* open server fifo
* success : fd failed : -1
*/
int open_client_fifo(char *pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No server fifo to open\n");
return -1;
}
client_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(client_write_fd < 0){
perror("Can't open server fifo error");
return -1;
}
if((flags = fcntl(client_write_fd, F_GETFL, 0)) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(client_write_fd, F_SETFL, flags) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
return 0;
error:
close(client_write_fd);
return -1;
}
/*
* open server fifo for reading
* success : fd failed : -1
*/
int open_server_fifo(char * pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No client fifo to open");
return -1;
}
server_read_fd = open(pipe_name, O_RDONLY|O_NONBLOCK);
if(server_read_fd < 0){
printf("Can't open client fifo for reading");
return -1;
}
server_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(server_write_fd < 0){
printf("Can't open client fifo for writing");
return -1;
}
if((flags = fcntl(server_read_fd, F_GETFL, 0)) < 0){
printf("Client fifo F_GETFL error");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(server_read_fd, F_SETFL, flags) < 0){
printf("Client fifo F_SETFL error");
goto error;
}
return 0;
error:
close(server_read_fd);
close(server_write_fd);
return -1;
}
int data_parse(void)
{
char *p, *postion;
p = recv_buf;
if(*p != DATA_SEPARATOR){
printf("command must begin with %c\n", DATA_SEPARATOR);
return -1;
}
p++;
postion = strchr(p, DATA_SEPARATOR);
if(postion == NULL){
printf("file separator missing in fifo command\n");
return -1;
}
strncpy(client_fifo_path, p, postion - p);
p = ++postion;
strncpy(content, p, strlen(p));
return 0;
}
int main(int argc, char *argv[])
{
int ret;
if(fifo_init()){
return -1;
}
if(open_server_fifo(SERVER_FIFO_PATH)){
goto error;
}
while(1){
memset(recv_buf, 0, RECV_BUF_SIZE);
if((ret = read(server_read_fd, recv_buf, RECV_BUF_SIZE)) > 0){
if(data_parse()){
continue;
}
printf("Receive data from <%s> : %s\n",
client_fifo_path, content);
if(open_client_fifo(client_fifo_path)){
continue;
}
sprintf(send_buf, "hello, %s\n", client_fifo_path);
ret = write(client_write_fd, send_buf, strlen(send_buf));
if(ret < 0){
perror("Write data to client fifo error");
continue;
}
}
}
exit(EXIT_SUCCESS);
error:
remove(SERVER_FIFO_PATH);
exit(EXIT_FAILURE);
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#define SERVER_FIFO_PATH "/tmp/fifo_server"
#define CLIENT_FIFO_PATH "/tmp/fifo_client_%d"
#define SEND_BUF_SIZE 1024
#define RECV_BUF_SIZE 1024
#define CLIENT_FIFO_SIZE 128
int server_write_fd, client_read_fd, client_write_fd;
char client_fifo[CLIENT_FIFO_SIZE];
char send_buf[SEND_BUF_SIZE];
static int fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP;
/*
* create client fifo
* success : 0 failed : -1
*/
int fifo_init(void)
{
int ret;
struct stat filestat;
memset(client_fifo, 0, CLIENT_FIFO_SIZE);
sprintf(client_fifo, CLIENT_FIFO_PATH, getpid());
ret = stat(client_fifo, &filestat);
if(ret == 0){
if (unlink(client_fifo) < 0){
perror("Cannot delete old MI fifo");
return -1;
}
}else if(ret < 0 && errno != ENOENT){
perror("MI FIFO stat failed");
return -1;
}
if((mkfifo(client_fifo, fifo_mode) < 0)){
perror("Create client fifo error");
return -1;
}
if((chmod(client_fifo, fifo_mode) < 0)){
perror("Chmod client fifo error");
goto error;
}
return 0;
error:
remove(client_fifo);
return -1;
}
/*
* open server fifo for writing
* success : fd failed : -1
*/
int open_server_fifo(char *pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No server fifo to open\n");
return -1;
}
server_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(server_write_fd < 0){
perror("Can't open server fifo error");
return -1;
}
if((flags = fcntl(server_write_fd, F_GETFL, 0)) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(server_write_fd, F_SETFL, flags) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
return 0;
error:
close(server_write_fd);
return -1;
}
/*
* open client fifo
* success : fd failed : -1
*/
int open_client_fifo(char * pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No client fifo to open.\n");
return -1;
}
client_read_fd = open(pipe_name, O_RDONLY|O_NONBLOCK);
if(client_read_fd < 0){
printf("Can't open client fifo for reading");
return -1;
}
client_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(client_write_fd < 0){
printf("Can't open client fifo for writing\n");
return -1;
}
if((flags = fcntl(client_read_fd, F_GETFL, 0)) < 0){
printf("Client fifo F_GETFL error.\n");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(client_read_fd, F_SETFL, flags) < 0){
printf("Client fifo F_SETFL error.\n");
goto error;
}
return 0;
error:
close(client_read_fd);
close(client_write_fd);
return -1;
}
int main(int argc, char *argv[])
{
int ret;
char recv_buf[RECV_BUF_SIZE];
if(fifo_init()){
return -1;
}
if(open_client_fifo(client_fifo)){
goto error1;
}
if(open_server_fifo(SERVER_FIFO_PATH)){
goto error2;
}
sprintf(send_buf, ":%s:hello, server.", client_fifo);
ret = write(server_write_fd, send_buf, strlen(send_buf));
if(ret < 0){
perror("Write data to server fifo error");
goto error3;
}
memset(recv_buf, 0, RECV_BUF_SIZE);
if((ret = read(client_read_fd, recv_buf, RECV_BUF_SIZE)) > 0){
printf("Receive data form server : %s", recv_buf);
}
close(client_read_fd);
close(server_write_fd);
remove(client_fifo);
exit(EXIT_SUCCESS);
error3:
close(server_write_fd);
error2:
close(client_read_fd);
close(client_write_fd);
error1:
remove(client_fifo);
exit(EXIT_FAILURE);
}
select版本
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#define RECV_BUF_SIZE 1024
#define SEND_BUF_SIZE 1024
#define CONTENT_LEN 128
#define ARGUMENT_LEN 128
#define DATA_SEPARATOR ':'
#define SERVER_FIFO_PATH "/tmp/fifo_server"
int server_write_fd, server_read_fd, client_write_fd;
char send_buf[SEND_BUF_SIZE];
char content[CONTENT_LEN];
char client_fifo_path[ARGUMENT_LEN];
char recv_buf[RECV_BUF_SIZE];
char send_buf[SEND_BUF_SIZE];
static int fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP;
/*
* create server fifo
* success : 0 failed : -1
*/
int fifo_init(void)
{
int ret;
struct stat filestat;
ret = stat(SERVER_FIFO_PATH, &filestat);
if(ret == 0){
if (unlink(SERVER_FIFO_PATH) < 0){
perror("Cannot delete old MI fifo");
return -1;
}
}else if(ret < 0 && errno != ENOENT){
perror("MI FIFO stat failed");
return -1;
}
if((mkfifo(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Create client fifo error");
return -1;
}
if((chmod(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Chmod client fifo error");
goto error;
}
return 0;
error:
remove(SERVER_FIFO_PATH);
return -1;
}
/*
* open server fifo
* success : fd failed : -1
*/
int open_client_fifo(char *pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No server fifo to open\n");
return -1;
}
client_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(client_write_fd < 0){
perror("Can't open server fifo error");
return -1;
}
if((flags = fcntl(client_write_fd, F_GETFL, 0)) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(client_write_fd, F_SETFL, flags) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
return 0;
error:
close(client_write_fd);
return -1;
}
/*
* open server fifo for reading
* success : fd failed : -1
*/
int open_server_fifo(char * pipe_name)
{
//int flags;
if(!pipe_name || *pipe_name == 0){
printf("No client fifo to open");
return -1;
}
server_read_fd = open(pipe_name, O_RDONLY|O_NONBLOCK);
if(server_read_fd < 0){
printf("Can't open client fifo for reading");
return -1;
}
return 0;
}
int data_parse(void)
{
char *p, *postion;
p = recv_buf;
if(*p != DATA_SEPARATOR){
printf("command must begin with %c\n", DATA_SEPARATOR);
return -1;
}
p++;
postion = strchr(p, DATA_SEPARATOR);
if(postion == NULL){
printf("file separator missing in fifo command\n");
return -1;
}
strncpy(client_fifo_path, p, postion - p);
p = ++postion;
strncpy(content, p, strlen(p));
return 0;
}
int main(int argc, char *argv[])
{
int ret, maxfd;
fd_set set, read_set;
struct timeval timeout;
if(fifo_init()){
return -1;
}
if(open_server_fifo(SERVER_FIFO_PATH)){
goto error;
}
FD_ZERO(&set);
FD_SET(server_read_fd, &set);
maxfd = server_read_fd + 1;
while(1){
read_set = set;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
memset(recv_buf, 0, RECV_BUF_SIZE);
ret = select(maxfd, &read_set, NULL, NULL, &timeout);
switch(ret){
case 0:
continue;
case -1:
perror("select");
continue;
default:
if(FD_ISSET(server_read_fd, &read_set)){
if((ret = read(server_read_fd,
recv_buf, RECV_BUF_SIZE)) > 0){
if(data_parse()){
continue;
}
printf("Receive data from <%s> : %s\n",
client_fifo_path, content);
if(open_client_fifo(client_fifo_path)){
continue;
}
sprintf(send_buf, "hello, %s\n", client_fifo_path);
ret = write(client_write_fd,
send_buf, strlen(send_buf));
if(ret < 0){
perror("Write data to client fifo error");
continue;
}
}
}
}
}
remove(SERVER_FIFO_PATH);
exit(EXIT_SUCCESS);
error:
remove(SERVER_FIFO_PATH);
exit(EXIT_FAILURE);
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#define RECV_BUF_SIZE 1024
#define SEND_BUF_SIZE 1024
#define CONTENT_LEN 128
#define ARGUMENT_LEN 128
#define DATA_SEPARATOR ':'
#define SERVER_FIFO_PATH "/tmp/fifo_server"
int server_write_fd, server_read_fd, client_write_fd;
char send_buf[SEND_BUF_SIZE];
char content[CONTENT_LEN];
char client_fifo_path[ARGUMENT_LEN];
char recv_buf[RECV_BUF_SIZE];
char send_buf[SEND_BUF_SIZE];
static int fifo_mode = S_IRUSR| S_IWUSR| S_IRGRP| S_IWGRP;
/*
* create server fifo
* success : 0 failed : -1
*/
int fifo_init(void)
{
int ret;
struct stat filestat;
ret = stat(SERVER_FIFO_PATH, &filestat);
if(ret == 0){
if (unlink(SERVER_FIFO_PATH) < 0){
perror("Cannot delete old MI fifo");
return -1;
}
}else if(ret < 0 && errno != ENOENT){
perror("MI FIFO stat failed");
return -1;
}
if((mkfifo(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Create client fifo error");
return -1;
}
if((chmod(SERVER_FIFO_PATH, fifo_mode) < 0)){
perror("Chmod client fifo error");
goto error;
}
return 0;
error:
remove(SERVER_FIFO_PATH);
return -1;
}
/*
* open server fifo
* success : fd failed : -1
*/
int open_client_fifo(char *pipe_name)
{
int flags;
if(!pipe_name || *pipe_name == 0){
printf("No server fifo to open\n");
return -1;
}
client_write_fd = open(pipe_name, O_WRONLY|O_NONBLOCK);
if(client_write_fd < 0){
perror("Can't open server fifo error");
return -1;
}
if((flags = fcntl(client_write_fd, F_GETFL, 0)) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
flags&=~O_NONBLOCK;
if(fcntl(client_write_fd, F_SETFL, flags) < 0){
perror("Server fifo F_GETFL error");
goto error;
}
return 0;
error:
close(client_write_fd);
return -1;
}
/*
* open server fifo for reading
* success : fd failed : -1
*/
int open_server_fifo(char * pipe_name)
{
//int flags;
if(!pipe_name || *pipe_name == 0){
printf("No client fifo to open");
return -1;
}
server_read_fd = open(pipe_name, O_RDONLY|O_NONBLOCK);
if(server_read_fd < 0){
printf("Can't open client fifo for reading");
return -1;
}
return 0;
}
int data_parse(void)
{
char *p, *postion;
p = recv_buf;
if(*p != DATA_SEPARATOR){
printf("command must begin with %c\n", DATA_SEPARATOR);
return -1;
}
p++;
postion = strchr(p, DATA_SEPARATOR);
if(postion == NULL){
printf("file separator missing in fifo command\n");
return -1;
}
strncpy(client_fifo_path, p, postion - p);
p = ++postion;
strncpy(content, p, strlen(p));
return 0;
}
int main(int argc, char *argv[])
{
int ret, maxfd;
fd_set set, read_set;
struct timeval timeout;
if(fifo_init()){
return -1;
}
if(open_server_fifo(SERVER_FIFO_PATH)){
goto error;
}
FD_ZERO(&set);
FD_SET(server_read_fd, &set);
maxfd = server_read_fd + 1;
while(1){
read_set = set;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
memset(recv_buf, 0, RECV_BUF_SIZE);
ret = select(maxfd, &read_set, NULL, NULL, &timeout);
switch(ret){
case 0:
continue;
case -1:
perror("select");
continue;
default:
if(FD_ISSET(server_read_fd, &read_set)){
if((ret = read(server_read_fd,
recv_buf, RECV_BUF_SIZE)) > 0){
if(data_parse()){
continue;
}
printf("Receive data from <%s> : %s\n",
client_fifo_path, content);
if(open_client_fifo(client_fifo_path)){
continue;
}
sprintf(send_buf, "hello, %s\n", client_fifo_path);
ret = write(client_write_fd,
send_buf, strlen(send_buf));
if(ret < 0){
perror("Write data to client fifo error");
continue;
}
}
}
}
}
remove(SERVER_FIFO_PATH);
exit(EXIT_SUCCESS);
error:
remove(SERVER_FIFO_PATH);
exit(EXIT_FAILURE);
}