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

时间:2021-12-29 00:55:02

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_BUFint 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系统中的实现)在程序之间传递数据是很有效率的.