进程间通信之管道

来源:互联网 发布:js代码攻击 编辑:程序博客网 时间:2024/05/22 14:24

管道:

管道是进程间通信的主要手段之一。
一个管道实际上就是个只存在于内存中的文件,
对这个管道的操作需要两个已经打开的文件(两个文件描述符),代表管道的两端。

管道根据其适用范围可分为:
匿名管道和命名管道。

管道容量:

管道容量分为pipe buf和pipe capacity两种:

1.先来看看第一种pipe buf:
pipe buf定义的是内核管道缓冲区的容量,这个值是由内核来设定的。

可以通过一条命令来查看:

ulimit -a

可以计算出:
管道容量:sizeof(pipe_buf) = 512bytes * 8 = 4kb.

这里写图片描述

2,pipe capacity:

当管道满的时候:
O_NONBLOCK disable: write调用阻塞,直到有进程读走管道中的数据
O_NONBLOCK enable:调用返回-1,errno值设为EAGAIN

由下面的程序来测试这个值的大小:

这里写图片描述

结果显示:
写满之后,一个进程阻塞式等待另一个进程读取管道内的数据。

pipe capacity的大小为65536。

这里写图片描述

管道缓冲区:

 匿名管道由一个在基本文件系统存储设备上的INODE,一个与其相连的内存INODE,两个打开文件控制块(分别对应管道的信息发送端和信息接收端)及其所属进程的描述信息来标识,在系统执行PIPE(P)命令行之后生成。并在P[0]中返回管道的读通道打开文件描述等,在P[1]中返回管道的写通道打开文件描述符。

从结构上看,匿名管道没有文件路径名,不占用文件目录项,因此文件目录结构中的链表不适用于这种文件,它只是存在于打开文件结构中的一个临时文件,随其所依附的进程的生存而生存,当进程终止时,匿名管道也随之消亡。——–>称之为随进程。

送入管道的信息一旦被读进程取用就从管道中消失了,读写操作之间符合先进先出的队列原则。

管道文件的同步与互斥:

    管道文件是进程间通信的工具,为了尽量少的占用系统存储资源,一般系统均将其限制为最大长度为4096(PIPSIZ)字节的小型文件。当欲写入的消息超过4096字节时,就产生了读、写进程之间的`同步问题。`首先写操作查找PIPE文件中当前指针的偏移量F-OFFSET,然后从此位置开始尽量写入信息,当长度达到4096字节时,系统控制写进程进入睡眠状态,一直等待读进程取走全部信息时,文件长度指针置0,写进程才被唤醒继续工作。为防止多个进程同时读写一个管道文件而产生混乱,在管道文件的INODE标志字I-FLAY项中设置了ILOCK标志项,以设置软件锁的方式实现多进程间对管道文件的`互斥使用`。

这里写图片描述

匿名管道:

本质:

匿名管道之所以可以通信是因为子进程和父进程具有相同的文件描述符表,所以父子进程可以看到一份公共的资源。

单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

描述信息:
主要用于具有亲缘关系的进程(主要是父子进程,当然了,兄弟进程也可以啦)
可以通过系统调用建立起单向的通信管道。且这种关系只能由父进程来建立。
因为这种管道是单向通信的,所以当需要双向通信时,就要创建两个管道。

匿名管道的创建:

#include <unistd.h>int pipe(int fd[2])

可以达到进程间通信的三个步骤:

1.创建管道(pipe)
打开文件(以读和写的方式)–>以输出型参数的形式返回文件描述符(3和4)。

2.fork()子进程—-》创建子进程
子进程和父进程的文件描述符表相同—->所以父子进程才可以在匿名管道中通信。

3.关闭部分的文件描述符—->起到预防作用。
(因为管道是单方向进行通信的)
如果不关闭管道,父子进程也是可以进行通信的。

匿名管道的五个特点:

1.匿名管道支持单方向通信。

2.管道通信依赖于文件系统。
所以管道的声明周期是随进程。—–>进程一旦结束,管道也就随之而被释放。

3.匿名管道只能用于两个具有亲缘关系的两个进程。(例如父子进程)

4.匿名管道是按照字节流的方式来进行读写的。(没有格式要求)

5.管道自带同步机制。(读写的顺序一致)

>

命名管道:

本质:
命名管道之所以可以实现进程间通信是通过同一个路径名从而看到了同一份资源。这份资源以FIFO的文件形式存在于文件系统中。

这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。

命名管道的创建:

#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char * pathname, mode_t mode)

参数说明:

该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同(打开文件的权限)。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等

命名管道是为了解决无名管道只能用于有血缘关系的进程之间通信的缺陷而设计的。
命名管道是建立在实际的磁盘介质或文件系统(而不是只存在于内存中)上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。

实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但是仅是一个节点而已,文件的数据还是存在于内存缓冲页面中,和普通管道相同。

分析匿名管道和命名管道:

匿名管道存在着如下两个严重的缺点。
第一,匿名管道只能用于连接具有共同祖先的进程。
第二,匿名管道是依附进程而临时存在的。所以后来推出了一种无名管道的变种-有名管道,它常被称为FIFO。有名管道除继承了无名管道的所有特性优点之外,还屏弃了无名管道的两个缺点。

首先,FIFO是一种永久性的机构,它具有普通的UNIX系统文件名。在系统下可利用MKNOD命令建立永久的管道,除非刻意删除它,否则它将一直保持在系统中。

其次,正是由于有名管道以“文件名”来标识,所以只要事先约定某一特定文件名,那样所有知道该约定的服务进程,不论它们之间是否有亲属关系,都可以便利地利用管道进行通信。

命名管道文件被创建后,一些进程就可以不断地将信息写入命名管道文件里,而另一些进程也可以不断地从命名管道文件中读取信息。对命名管道文件的读写操作是可以同时进行的。

原创粉丝点击