进程通信—管道
概述
pipe函数创建一个管道,一个可用于进程间通信的单向数据通道。 数组pipefd用于返回两个指向管道两端的文件描述符。 pipefd [0]指的是管道的读取端。 pipefd [1]指的是管道的写入端。 写入管道写入端的数据由内核缓冲,直到从管道的读取端读取。
接口说明
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
如果flags为0,则pipe2()与pipe()相同。 以下值可以在标志中按位或运算以获得不同的行为:
O_CLOEXEC
在两个新的文件描述符上设置close-on-exec(FD_CLOEXEC)标志。 请参阅open函数中同一个标志的说明,这可能有用。
O_DIRECT(自Linux 3.4开始)
创建一个管道,以“数据包”模式执行I / O。对管道的每个写入(2)都作为一个单独的数据包处理,并且从管道读取(2)将一次读取一个数据包。请注意以下几点:
*大于PIPE_BUF字节的写入将被拆分为多个数据包。常量PIPE_BUF在<limits.h>中定义。
*如果read指定的缓冲区大小小于下一个数据包,则读取请求的字节数,并丢弃数据包中的超出字节。指定PIPE_BUF的缓冲区大小将足以读取最大可能的数据包。
*不支持零长度数据包。 (指定缓冲区大小为零的read是no-op,并返回0.)
从Linux 4.5开始,可以使用fcntl函数来更改管道文件描述符的O_DIRECT设置。
O_NONBLOCK
在两个新的打开文件描述中设置O_NONBLOCK文件状态标志。 使用此标志可以节省对fcntl(2)的额外调用,以实现相同的结果。
管道的局限性
1)管道只能在公共的祖先的两个进程之间使用。
2)管道是单向的。
测试用例并说明
测试代码
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
static void sig_catch(int signo)
{
printf("signal caught\n");
exit(0);
}
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe2(pipefd,O_NONBLOCK) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child write from pipe */
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = sig_catch;
if (sigaction(SIGPIPE, &sa, NULL) < 0)
printf("sigaction failed\n");
printf("4\n");
close(pipefd[0]); /* Close unused read end */
if(write(pipefd[1], argv[1], strlen(argv[1]))==-1){
printf("5\n");
}
close(pipefd[1]); /* Reader will see EOF */
exit(EXIT_SUCCESS);
} else { /* Parent read argv[1] to pipe */
printf("1\n");
close(pipefd[1]); /* Close unused write end */
printf("2\n");
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
printf("3\n");
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
exit(EXIT_SUCCESS);
}
}
测试结果
[root@localhost ~]# ./a.out 323
1
2
3
4
signal caught
测试说明
本例子是用管道+非阻塞来实现的。父进程读取数据后就关闭了管道的读端。由于子进程已经关闭了读端,如果子进程如果再往读端写数据就会出错,并生成一个SIGPIPE信号。
参考资料
[1].http://man7.org/linux/man-pages/man7/pipe.7.html
[2]. http://man7.org/linux/man-pages/man2/pipe.2.html