linux亲缘进程通信之无名管道

无名管道通信,只能用于父子进程、兄弟进程等具有亲缘关系的进程之间通信。原因也很简单,无名管道文件被放置在内存中虚拟的文件系统中,只有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函数相同,但分别调用openfcnt两个函数,不是原子操作,多线程环境中存在竞态条件,故用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的数据为止


猜你喜欢

转载自blog.csdn.net/qq_31073871/article/details/80889801