无名管道通信,只能用于父子进程、兄弟进程等具有亲缘关系的进程之间通信。原因也很简单,无名管道文件被放置在内存中虚拟的文件系统中,只有fork/vfork分裂出的亲缘进程,才能继承到无名管道描述符(文件描述符)。
重要的系统调用:pipe( )和pipe2( )
int pipe2(int pipefd[2], int flags);
int pipe(int pipefd[2]);
注:pipe2的flags填0时,等价于pipe
形参:@pipefd 输出pipe/pipe2创建的管道文件描述符pipe file descriptor
@flags 可按位与以下两个值:
O_NONBLOCK 设置为非堵塞模式,fcntl( )函数也可实现该功能
O_CLOEXEC 从字面意思来看,该标志的功能是,在fork出的子进程中执行exec函数族时,自动关闭管道文件描述符。
返回:成功返回0,失败返回-1并设置errno
关于O_CLOEXEC 标志的更详细的讲解:https://blog.csdn.net/ubuntu_hao/article/details/51393632
- 调用
open
函数O_CLOEXEC
模式打开的文件描述符在执行exec
调用新程序中关闭,且为原子操作。 - 调用
open
函数不使用O_CLOEXEC
模式打开的文件描述符,然后调用fcntl
函数设置FD_CLOEXEC
选项,效果和使用O_CLOEXEC
选项open
函数相同,但分别调用open
、fcnt
两个函数,不是原子操作,多线程环境中存在竞态条件,故用open
函数O_CLOEXEC
选项代替之。 - 调用
open
函数O_CLOEXEC
模式打开的文件描述符,或是使用fcntl
设置FD_CLOEXEC
选项,这二者得到(处理)的描述符在通过fork
调用产生的子进程中均不被关闭。 - 调用
dup
族类函数得到的新文件描述符将清除O_CLOEXEC
模式。
使用非常简单,直接给出例子:
#define R 0
#define W 0
int fd[2];//无名管道描述符
char buf_rcv[10];
pipe(fd);//建立堵塞型无名管道,并把管道描述符返回到fd数组,fd[0]用于读管道,fd[1]用于写管道
if( fork() > 0 )
{ /*父进程*/
char *buf_snd[]="hello";
write(fd[W], buf_snd, sizeof(buf_snd));//使用文件操作,来操作无名管道。若管道空间已被写满,且无人读走,那么write操作会堵塞
close(fd[W]);
}
else
{
/*子进程*/
read(fd[R], buf_rcv, 10);//read一直堵塞,直到能从管道读出数据,才解除堵塞
printf("%5s", buf_rcv);
close(fd[R]);
}
用起来很简单,注意事项却不少:
(1)fd[0]是只读的,试图写入将会发生错误,并设置errno;fd[1]同理;
(2)在pipe的读端被关闭后,向pipe写数据,将会触发SIFPIPE信号,该信号的默认处理方式为进程终止!也即,写端依赖读端;
(3)管道所能容纳的最大字节数PIPE_BUF被定义在linux/limits.h,值的大小跟内核版本有关,自行查阅源文件即可。pipe被填满之后,再向里write数据,那么write会被堵塞,直到有空闲空间可以容纳write的数据为止