Linux进程间通信(IPC)——管道和FIFO
管道和FIFO
一、 匿名管道
#include <unistd.h>
int pipe(int fd[2]); /* fd[0] 管道输出端FD, fd[1]管道输入端FD */
int pipe2(int pipefd[2], int flags); /* flags:O_NONBLOCK */
二、FIFO(有名管道,半双工)
支持同台主机内无亲缘关系的进程间建立管道。只能用在本地文件系统中,不适用NFS。
- 创建FIFO
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
若pathname已经存在,返回-1,errno为 EEXIST。创建FIFO后,可用open、或者标准IO,如fopen打开,一个fd或为读、或为写。
注意:open为读时会阻塞(以O_NONBLOCK打开除外),直到有open为写;反之亦然。
举例:如下代码会引起死锁
/* Process 1 */
readfd = open(FIFO_PATH1, O_RDONLY, 0); /* 阻塞直到有 open FIFO_PATH1为写 */
writefd = open(FIFO_PATH2, O_WRONLY, 0);
/* Process 2*/
readfd = open(FIFO_PATH2, O_RDONLY, 0); /* 阻塞直到有 open FIFO_PATH2为写 */
writefd = open(FIFO_PATH1, O_WRONLY, 0);
- 销毁FIFO
只能用unlink unlinkat 将FIFO名字从文件系统中删除。
#include <unistd.h>
int unlink(const char *pathname);
#include <fcntl.h> /* Definition of AT_* constants */
#include <unistd.h>
int unlinkat(int dirfd, const char *pathname, int flags);
- FIFO 一读多写
/* 服务端 FIFO读端 */
iret = mkfifo("/tmp/fifo.server", 0777);
readfd = open("/tmp/fifo.server", O_RDONLY, 0);
/* 始终保持一个写端,防止每次客户端关闭写端后,fifo读空后,read一直返回0,不会阻塞读其他客户端的写 */
dummyfd = open("/tmp/fifo.server", O_WRONLY, 0);
while(1)
{
iret = read(readfd, buf, 1024);
}
/* 客户端 FIFO写端 */
writefd = open("/tmp/fifo.server", O_WRONLY, 0);
iret = write(writefd, buf, strlen(buf));
close(writefd); /* 写端关闭,客户端退出 */
exit(0);
三、popen 和 pclose
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
type 为 r,调用进程读进 command的标准输出;
type 为 w, 调用进程写到 command的标准输入。
四、Unix域套接字(d管道)
socketpair可以创建一对无命名的、相互连接的Unix域套接字,起到双向管道的作用,其中type:SOCK_STREAM、SOCK_DGRAM。因为无命名,所以只能用于有亲缘关系的进程间通信。
UNIX域数据报服务是可靠的,可以保持消息边界。
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(AF_UNIX, int type, int protocol, int fd[2]);
五、管道(pipe)和FIFO的属性
- 管道(含FIFO)的容量
(1)管道容量的大小默认64KB,/proc/sys/fs/pipe-max-size since Linux 2.6.35,规定了可设置的最大容量大小;
(2)设备/获取管道容量的大小:
#include <unistd.h>
#include <fcntl.h>
iret = fcntl(pipefd[i], F_SETPIPE_SZ, lSize);
lSize = fcntl(pipefd[i], F_GETPIPE_SZ);
-
读写时另一端关闭
(1) read一个写端已被关闭的管道时, 在所有数据都被读取后,read返回0。
(2) write一个读端已被关闭的管道(或者socket),则产生信号SIGPIPE,如果忽略该信号或者捕捉该信号从其处理函数返回后,write返回-1,errno值为EPIPE。 -
管道和FIFO的原子性
man 7 pipe查看如下:O_NONBLOCK disabled, n <= PIPE_BUF 空间不足写入时,阻塞。 All n bytes are written atomically; write(2) may block if there is not room for n bytes to be writ‐ ten immediately O_NONBLOCK enabled, n <= PIPE_BUF 空间不足写入时,返回失败,1个字节都不会写入。 If there is room to write n bytes to the pipe, then write(2) succeeds immediately, writing all n bytes; otherwise write(2) fails, with errno set to EAGAIN. O_NONBLOCK disabled, n > PIPE_BUF 不能保证全部数据原子性写入,write阻塞直到全部数据被写入 The write is nonatomic: the data given to write(2) may be interleaved with write(2)s by other process; the write(2) blocks until n bytes have been written. O_NONBLOCK enabled, n > PIPE_BUF 1个字节空间都没有时 write返回失败,否则返回实际能写入的字节数。 If the pipe is full, then write(2) fails, with errno set to EAGAIN. Otherwise, from 1 to n bytes may be written (i.e., a "partial write" may occur; the caller should check the return value from write(2) to see how many bytes were actually written), and these bytes may be interleaved with writes by other processes.
-
获取PIPE_BUF
(1)ulimit -a
(2)动态获取
#include <unistd.h>
long pathconf(fifo_path, _PC_PIPE_BUF); /* 一般为4KB */
(3) shell命令
getconf PIPE_BUF fifo_path
- 获取OPEN_MAX(进程可打开的最大FD数量)
(1) 动态获取
(2) shell命令
getconf OPEN_MAX
- IO复用调度处理
当发生读写错误时(一端关闭),select(poll、epoll同样)返回说相应的FD是可读的(或可写的),真正的错误则由read(或write)返回。
另外,unlink后的路径再open会失败,但不影响在unlink前open获取的FD。