linux程序设计——对FIFO开展读写操作(第十三章)

linux程序设计——对FIFO进行读写操作(第十三章)

4.对FIFO进行读写操作

使用O_NONBLOCK模式会影响的对FIFO的read和write调用.
对一个空的,阻塞的FIFO
(即没有用O_NONBLOCK标志打开)的read调用将等待,直到有数据可以读时才继续执行.与此相反,对一个空的,非阻塞的FIFO的read调用将立刻返回0字节.
对一个完全阻塞的FIFO的write调用将等待,直到数据可以写入时才继续执行
.如果非阻塞的FIFO不能接收所有写入的数据,它将按下面的规则执行.
如果请求写入的数据长度小于等于PIPE_BUF字节,调用失败,数据不能写入.
如果请求写入的数据长度大于PIPE_BUF字节,将写入部分数据,返回实际写入的字节数,返回值也可能是0.
FIFO的长度是需要考虑的一个很重要的因素,系统对任一时刻在一个FIFO中可以存在的数据长度是有限制的,它由#define PIPE_BUF语句定义,通常可以在头文件limits.h中找到它.在linux和许多其他类UNIX系统中,它的值通常是4096字节,但在某些系统中它可能会小到512字节.系统规定:在一个以O_WRONLY方式(即阻塞方式)打开的FIFO中,如果写入的数据小于等于PIPE_BUF,那么或者写入全部字节,或者一个字节都不写入.
虽然,对只有一个FIFO写进程和一个FIFO读进程的简单情况来说,这个限制并不是非常重要的,但只使用一个FIFO并允许多个不同的程序向一个FIFO读进程发送请求的情况是很就常见的.如果几个不同的程序尝试同时向FIFO写数据,能否保证来自不同程序的数据库不相互交错就非常关键了.也就是说,每个写操作都必须是"原子化"的.怎样才能做到这一点呢?
如果能保证所有的写请求是发往一个阻塞的FIFO的,并且每个请求的数据长度小于等于PIPE_BUF字节,系统就可以确定数据块不会交错在一起.通常将每次通过FIFO传递的数据长度限制为PIPE_BUF字节是个好办法,除非只使用一个写进程和读进程.
使用FIFO实现进程间通信
为了演示不相关的进程是如何使用命名管道进行通信的,需要用到两个独立的程序fifo3.c和fifo4.c
/*************************************************************************
 > File Name:    fifo3.c
 > Description:  fifo3.c程序是生产者程序,它在需要时创建管道,然后尽可能块地向管道中写入数据
 > Author:       Liubingbing
 > Created Time: 2015年07月14日 星期二 21时28分28秒
 > Other:        fifo3.c
 ************************************************************************/

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

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)

int main()
{
	int pipe_fd;
	int res;
	int open_mode = O_WRONLY;
	int bytes_sent = 0;
	char buffer[BUFFER_SIZE + 1];

	/* 检查FIFO_NAME文件是否存在,如果不存在就创建它 */
	if (access(FIFO_NAME, F_OK) == -1) {
		/* mkfifo创建命名管道(即特殊类型的文件FIFO) */
		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_OWRONLY\n", getpid());

	/* open函数以O_WRONLY方式打开FIFO文件,如果成功pipe_fd指向打开的文件 */
	pipe_fd = open(FIFO_NAME, open_mode);
	printf("Process %d result %d\n", getpid(), pipe_fd);

	if (pipe_fd != -1) {
		/* */
		while(bytes_sent < TEN_MEG) {
			/* write函数从buffer指向的内存中写入BUFFER_SIZE个字节到pipe_fd文件中
			 * 如果成功则返回实际写入的字节数 */
			res = write(pipe_fd, buffer, BUFFER_SIZE);
			if (res == -1) {
				fprintf(stderr, "Write error on pipe\n");
				exit(EXIT_FAILURE);
			}
			bytes_sent += res;
		}
		(void)close(pipe_fd);
	} else {
		exit(EXIT_FAILURE);
	}
	
	printf("Process %d finished\n", getpid());
	exit(EXIT_SUCCESS);
}
第一个程序是生产者程序,它在需要时创建管道,然后尽可能地向管道中写入数据
/*************************************************************************
 > File Name:    fifo4.c
 > Description:  fifo4.c程序是消费者程序,它从FIFO读取数据并丢弃它们
 > Author:       Liubingbing
 > Created Time: 2015年07月14日 星期二 22时07分34秒
 > Other:        fifo4.c
 ************************************************************************/

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

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF

int main()
{
	int pipe_fd;
	int res;
	int open_mode = O_RDONLY;
	char buffer[BUFFER_SIZE + 1];
	int bytes_read = 0;

	memset(buffer, '\0', sizeof(buffer));
	
	printf("Process %d opening FIFO O_RDONLY\n", getpid());
	/* open函数打开FIFO_NAME文件,以open_mode的方式(即O_RDONLY)
	 * 如果成功,则返回文件描述符 */
	pipe_fd = open(FIFO_NAME, open_mode);
	printf("Process %d result %d\n", getpid(), pipe_fd);

	if (pipe_fd != -1) {
		do {
			/* read函数从pipe_fd指向的文件中读入BUFFER_SIZE个字节的数据到buffer指向的内存 
			 * 如果成功,返回实际读入数据的字节数 */
			res = read(pipe_fd, buffer, BUFFER_SIZE);
			bytes_read += res;
		} while (res > 0);
		(void)close(pipe_fd);
	} else {
		exit(EXIT_FAILURE);
	}

	printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
	exit(EXIT_SUCCESS);
}
第二个程序是消费者程序,它的代码要简单的多,它从FIFO读取数据并丢弃它们.
在运行这两个程序的同时,用time命令对读进程进行计时,输出结果如下所示:
$ ./fifo3 &
$ time ./fifo4
linux程序设计——对FIFO开展读写操作(第十三章)
两个程序使用的都是阻塞模式的FIFO,首先启动fifo3(写进程/生产者),它将阻塞以等待读进程打开这个FIFO.fifo4(消费者)启动以后,写进程解除阻塞并开始向管道写数据.同时,读进程也开始从管道中读取数据.
linux会安排好这两个进程之间的调度,使它们在可以运行的时候运行,在不能运行的时候阻塞.因此,写进程将在管道满时阻塞,读进程将在管道空时阻塞.
time命令的输出显示,读进程只运行了不到0.1秒的时间,却读取了10MB的数据,这说明管道(至少在现在Linux系统中的实现)在程序之间传递数据是很有效率的.

版权声明:本文为博主原创文章,未经博主允许不得转载。