进程间通信(六)
读取与写入FIFO
使用O_NONBLOCK模式会影响作用在FIFO上的read与write调用的行为。
在一个空的阻塞FIFO(例如,没有使用O_NONBLOCK打开的)上的read调用将会等待直到有数据可以读取。相反,在非阻塞且没有数据的FIFO上进行read调用将会返回0字节。
在一个完全阻塞的FIFO上的write调用将会等待直到数据可以写入。在一个不能全部接受所有将要写入数据的FIFO上的write调用将会:
如果请求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写入数据,由不同程序来的数据块交错在一起的情况是很严重的,每一个write操作都应是原子的。你认为呢?
然而,如是我们保证我们所有的write请求都发送到阻塞FIFO,而且在大小上小于PIPE_BUF字节,系统将会保证数据不会交错在一起。通常,严格限制通过FIFO发送PIPE_BUF字节大小的数据块是一个好主意,除非我们只使用一个读取端与一个写入端。
试验--使用FIFO的进程交互
要演示不相关的进程如何使用有名管道进行交互,我们需要两个单独的程序,fifo3.c与fifo4.c。
1 第一个程序是我们的生产者程序。如果需要他会创建管道,然后尽快向其中写入数据。
注意,为了演示的目的,我们并不介意数据是什么是,所以我们并不初始化一个缓冲区。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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];
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());
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)
{
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);
}
2 我们的第二个程序,消费者,比较简单。他由FIFO中读取并丢弃数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d/n",getpid(),pipe_fd);
if(pipe_fd != -1)
{
do
{
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);
}
当我们同时运行这些程序,使用time命令来统计读取的时间,我们会得到下面的输出:
$ ./fifo3 &
[1] 375
Process 375 opening FIFO O_WRONLY
$ time ./fifo4
Process 377 opening FIFO O_RDONLY
Process 375 result 3
Process 377 result 3
Process 375 finished
Process 377 finished, 10485760 bytes read
real 0m0.053s
user 0m0.020s
sys 0m0.040s
[1]+ Done fifo3