进程间通信(IPC):FIFO

FIFO又称命名管道,可以在无亲缘关系即不相关的进程间交换数据。

创建FIFO:

#include<sys.types.h>
#include<sys/stat.h>
int mkfifo(const char* pathname,mode_t mode);


创建FIFO后,可以用一般的文件io函数来操作,open,close,read,write,unlink等。

open(const char *path, O_RDONLY);//1  
open(const char *path, O_RDONLY | O_NONBLOCK);//2  
open(const char *path, O_WRONLY);//3  
open(const char *path, O_WRONLY | O_NONBLOCK);//4  

非阻塞标志O_NONBLOCK的影响

未指定O_NONBLOCK时,只读打开要阻塞到某个进程为写打开此FIFO;只写打开要阻塞到某个进程为读而打开此FIFO。

指定O_NOBLOCK时,只读,即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回只写,在没有进程为读而打开此FIFO时将会出错,其error为ENXIO。

 

类似于管道,写一个尚无进程为读而打开此FIFO将会产生信号SIGPIPE。若此FIFO的最后一个写进程关闭了此FIFO,则为该FIFO的读进程产生一个文件结束标志。类似与管道,常数PIPE_BUF说明可被原子写到FIFO的最大数据量

 

一个FIFO常有多个写进程,为避免写的穿插,需考虑原子写操作。

FIFO的两个用途:

1 FIFO由shell命令使用,将数据从一条管道传到另一条,无需中间临时文件

2 FIFO用于客户机-服务器应用程序中,以便客户机和服务器之间传递数据。

 

FIFO用于客户机-服务器中

一个服务器与多个客户机交换数据:

每个客户机将请求(包含客户机进程ID)写到该服务器创建的一个FIFO中。

于是此FIFO有多个写进程,客户机发送的请求应小于PIPE_BUF,才能原子写,不穿插。

服务器发出响应,为了让客户机知道哪个响应是自己请求的,服务器为每个客户机创建一个FIFO,使用的路径名包含客户机进程ID。如下图:

进程间通信(IPC):FIFO_第1张图片


不足:

服务器无法判断客户机是否崩溃终止,导致FIFO遗留在文件系统中。

服务器必须捕捉SIGPIPE信号,因为客户机可能发完请求后未读取响应就终止,留下一个有写进程但无读进程的FIFO,产生SIGPIPE信号。



例子:


#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

int main()
{
	const char *fifo_name = "/tmp/my_fifo";
	int pipe_fd = -1;
	int data_fd = -1;
	int res = 0;
	const int open_mode = O_WRONLY;
	int bytes_sent = 0;
	char buffer[PIPE_BUF + 1];

	if(access(fifo_name, F_OK) == -1)
	{
		//管道文件不存在
		//创建命名管道
		res = mkfifo(fifo_name, 0777);
		if(res != 0)
		{
			fprintf(stderr, "Could not create fifo %s\n", fifo_name);
			exit(EXIT_FAILURE);
		}
	}

	printf("Process %d opening FIFO O_WRONLY\n", getpid());
	//以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
	pipe_fd = open(fifo_name, open_mode);
	data_fd = open("Data.txt", O_RDONLY);
	printf("Process %d result %d\n", getpid(), pipe_fd);

	if(pipe_fd != -1)
	{
		int bytes_read = 0;
		//向数据文件读取数据
		bytes_read = read(data_fd, buffer, PIPE_BUF);
		buffer[bytes_read] = '\0';
		while(bytes_read > 0)
		{
			//向FIFO文件写数据
			res = write(pipe_fd, buffer, bytes_read);
			if(res == -1)
			{
				fprintf(stderr, "Write error on pipe\n");
				exit(EXIT_FAILURE);
			}
			//累加写的字节数,并继续读取数据
			bytes_sent += res;
			bytes_read = read(data_fd, buffer, PIPE_BUF);
			buffer[bytes_read] = '\0';
		}
		close(pipe_fd);
		close(data_fd);
	}
	else
		exit(EXIT_FAILURE);

	printf("Process %d finished\n", getpid());
	exit(EXIT_SUCCESS);
}

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>

int main()
{
	const char *fifo_name = "/tmp/my_fifo";
	int pipe_fd = -1;
	int data_fd = -1;
	int res = 0;
	int open_mode = O_RDONLY;
	char buffer[PIPE_BUF + 1];
	int bytes_read = 0;
	int bytes_write = 0;
	//清空缓冲数组
	memset(buffer, '\0', sizeof(buffer));

	printf("Process %d opening FIFO O_RDONLY\n", getpid());
	//以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
	pipe_fd = open(fifo_name, open_mode);
	//以只写方式创建保存数据的文件
	data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
	printf("Process %d result %d\n",getpid(), pipe_fd);

	if(pipe_fd != -1)
	{
		do
		{
			//读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
			res = read(pipe_fd, buffer, PIPE_BUF);
			bytes_write = write(data_fd, buffer, res);
			bytes_read += res;
		}while(res > 0);
		close(pipe_fd);
		close(data_fd);
	}
	else
		exit(EXIT_FAILURE);

	printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
	exit(EXIT_SUCCESS);
}




你可能感兴趣的:(ipc,fifo)