简述:Linux进程间通信--管道

Linux进程间通信之管道小记

1.无名管道

管道是一种基本的进程间通信机制,作用于亲缘进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。pipe函数原型如下:

    #include <unistd.h>
    int pipe(int pipefd[2]);     
    pipefd[0]:读端
    pipefd[1]:写端
    return: 0 成功   -1 失败

无名管道的一些特性:

  1. 管道在用户程序看起来在这里插入代码片就像一个打开的文件,通过read(filedes[0]),或者 write(filedes[1])向这个文件读写数据
  2. 实质上,管道是内核空间中固定大小的一块缓冲区;读写管道就是读写这块内核缓冲区
  3. 内核中使用环形队列机制,借助其缓冲区(4K)实现来实现管道;因而管道具有单向的数据流特性
  4. 管道不属于任何一种文件系统,所以不会写入到磁盘中;它只存在于内存中,并且是内核空间
  5. 这样就能理解为什么管道只能用于亲缘进程之间了;因为只存于内存中,每次pipe获取的管道都是不同的,只有从公共祖先中继承得来的管道才是相同的
  6. 写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据
  7. 管道的特殊性使得管道无法使用lseek函数在管道内跳转
  8. 理解管道的本质后,就能明白管道的许多特性了;如:
    数据一旦被读走,便不在管道中存在,不可反复读取
    这是因为管道是一个环形队列的机制,取出数据后就不存在队列中了
    属于半双工通信方式
    这不也是因为环形队列的原因么。。。
  9. 管道用于亲缘进程之间的通信,将管道看作一个特殊文件,充分体现了子进程继承父进程打开的文件的特性
  10. 管道pipe获取到的描述符pipefd[0],pipefd[1]与普通文件描述符没什么两样,并且是与普通文件描述符共用一个描述符队列
  11. 管道pipe可以使用close系统底层的IO操作关闭一个端口(读端/写端),或者两个端口都可以关闭
  12. 无名管道是随进程持续的,也就是说当最后一个打开该管道的进程退出后,管道也将消亡,不复存在
  13. 管道容量分为PIPE_CAPACITY 和 PIPE_BUF,这两者的区别在于PIPE_BUF定义的是内核管道缓冲区的大小,这个值的大小是由内核设定的(一般为4K);而PIPE_CAPACITY指的是管道的最大值,即容量,是内核内存中的一个缓冲区
  14. 管道的容量PIPE_CAPACITY就是说每次用pipe函数创建一个管道后能往其中写入的最大字节数(本机上经测试为65535)
  15. 管道的读写特性(读写端都打开着):当管道容量已满的时候,让其中写数据将会阻塞;管道为空时,读阻塞
  16. 当读写端有一端关闭后,有如下特性:
    管道写端被全部关闭且管道无数据,read返回0 (好像读到文件结尾)
    管道读端全部被关闭, 进程异常终止

管道
管道

下面是管道的数据结构实现上的描述,或许能让你有种恍然大悟的感觉:


在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。有两个 file 数据结构(代表两个文件描述符),但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。


2. 有名管道

有名管道是一种可以用于非亲缘关系进程之间通信的方式;也被称为FIFO文件,是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的无名管道类似。

使用有名管道首先需要创建一个有名管道文件,有如下两种方法:

  1. 通过命令创建:
mkfifo fifo
  1. 通过函数创建:
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);

mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask)

另外需要注意的是,共享文件夹是无法创建管道文件fifo的

有名管道的读写操作与无名管道一样,都是使用系统IO的读写操作:read/write;打开管道文件也是使用系统IO的打开函数:open,不过其参数有一些值得注意的地方:

int fifo_fd = open("xxx",flags);

flags的可选参数有:
O_RDWR
O_RDONLY
O_WRONLY
O_NONBLOCK

扫描二维码关注公众号,回复: 11857630 查看本文章

选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的

  1. 对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
  2. 对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。

有名管道还有其他特性:

  1. 有名管道文件不是一个普通文件,实际上还是一个内核中的缓冲区,所以不能用lseek操作有名管道文件
  2. 当我们往管道中写入了数据后,用ls -l fifo查看管道文件时,会发现管道文件的大小一直为0;这说明管道中的数据在文件系统中是不显像出来的,其数据还是通过内核的缓冲区来进行交换,只不过相对于无名管道而言多了一个文件名在文件系统中显现,故而叫有名管道

小结:通过管道可以对进程间通信以管窥豹,知道一些进程间通信的本质特征。

所有的进程间通信都是需要通过内核来进行的;因为不同进程间的虚拟内存空间是相互独立的,想要交换数据就要绕道内核空间,某个进程先把数据放到内核空间中,然后其他进程再从内核空间中获得数据。


参考:

  1. https://blog.csdn.net/qq_42914528/article/details/82023408
  2. https://blog.csdn.net/rannianzhixia/article/details/72793895
  3. 《UNIX高级编程》

猜你喜欢

转载自blog.csdn.net/weixin_41629848/article/details/97523339
今日推荐