Skip to content

LV005-管道简介

本文主要是进程通信——管道的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

一、Linux 管道

1. 无名管道

我们前边在学习Linux命令的时候,有学习过这么一个符号|,这就是管道,例如,

shell
ps -elf | grep a.out

它的功能是将前一个命令(ps -elf)的输出,作为后一个命令(grep a.out)的输入,不难看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行。上面这种管道是没有名字,所以|表示的管道称为匿名管道,也可以叫无名管道,用完了就销毁。

2. 有名管道

管道还有另外一个类型是命名管道,也被叫做 FIFO,也可以叫有名管道。因为数据是先进先出的传输方式。

2.1 创建有名管道

2.1.1 命令说明

在使用命名管道前,先需要通过 mkfifo 命令来创建,并且指定管道名字,可以在终端输入以下命令:

shell
mkfifo <管道名>
2.1.2 使用实例

我们可以执行以下命令:

shell
mkfifo myFifo

要是在Windows共享文件夹目录(/mnt/hgfs/Sharedfiles)下创建的话可能会收到如下提示:

shell
mkfifo: 无法创建先进先出文件'myFifo': 没有那个文件或目录

归根结底是因为用的是共享文件夹,而Windows的文件系统又不支持管道文件。创建的管道文件路径必须设为linux的本地文件夹。所以我们不要在共享目录下创建就可以啦,创建完成后我们使用ls -alh查看一下创建的文件详细信息:

shell
总用量 8.0K
drwxrwxr-x  2 hk hk 4.0K  6月  3 14:38 .
drwxr-x--- 22 hk hk 4.0K  6月  3 14:03 ..
prw-rw-r--  1 hk hk    0  6月  3 14:38 myFifo
-rw-rw-r--  1 hk hk    0  4月 23 22:04 test.vim

会发现,有名管道文件类型为p,也就是 pipe(管道) 的意思。

2.2 数据读写

2.2.1 命令说明
  • 向管道写入数据
shell
echo "data" > 已创建的管道名称 # 可以包含路径
  • 从管道读取数据
shell
cat < 已创建的管道名称  # 可以包含路径

【注意事项】我们必须在管道所在的目录下执行上边的读写命令,或者就是指明创建的管道的路径。

2.2.2 使用实例

下边的演示,在管道文件myFifo所在的目录中进行。我们在终端执行以下命令:

shell
echo "fanhua" > myFifo

输入命令后,按下回车,我们会发现终端停住了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出。我们开启另一个终端,输入以下命令:

shell
cat < myFifo

然后,终端会有以下数据输出:

shell
fanhua

这个时候,我们会发现,写入数据的终端命令正常退出了,管道中的数据已经读取了,我们再读会出现什么情况?管道中没有数据的时候再执行一次读取命令,那么这个终端就会停住,直到另一个终端向管道写入数据,便会直接读取数据然后退出。

我们可以看出,管道这种通信方式效率低,不适合进程间频繁地交换数据。当然,它的好处就是简单,同时也我们很容易得知管道里的数据已经被另一个进程读取了。

3. 管道大小查看

那么既然管道是一种文件,它的最大大小是多少呢?我们可以使用如下命令查看:

shell
ulimit -a

然后终端会有如下信息提示:

shell
real-time non-blocking time  (microseconds, -R) unlimited
core file size              (blocks, -c) 0
data seg size               (kbytes, -d) unlimited
scheduling priority                 (-e) 0
file size                   (blocks, -f) unlimited
pending signals                     (-i) 15308
max locked memory           (kbytes, -l) 498501
max memory size             (kbytes, -m) unlimited
open files                          (-n) 1024
pipe size                (512 bytes, -p) 8
POSIX message queues         (bytes, -q) 819200
real-time priority                  (-r) 0
stack size                  (kbytes, -s) 8192
cpu time                   (seconds, -t) unlimited
max user processes                  (-u) 15308
virtual memory              (kbytes, -v) unlimited
file locks                          (-x) unlimited

其中有一个pipe size,可以看到是8x521byte=4k。但是当我们查看man手册的时候,使用如下命令:

shell
man 7 pipe

然后找到Pipe capacity部分,有如下说明:

shell
A pipe  has a limited capacity.  If the pipe is full, then a write(2) will block or fail, depending on whether the O_NONBLOCK  flag  is  set (see  below).  Different implementations have different limits for the pipe capacity.  Applications should not rely on a particular capacity: an  application  should be designed so that a reading process consumes data as soon as it is available, so that a writing  process  does  not remain blocked.

In  Linux  versions before 2.6.11, the capacity of a pipe was the same as the system page size (e.g., 4096  bytes  on  i386).   Since  Linux 2.6.11,  the pipe capacity is 16 pages (i.e., 65,536 bytes in a system with a page size of 4096 bytes).  Since Linux 2.6.35, the default pipe capacity  is  16  pages, but the capacity can be queried and set using the fcntl(2) F_GETPIPE_SZ and F_SETPIPE_SZ operations.   See  fcntl(2) for more information.

其实看的不是很明白,后边挺老师讲的时候,说最大是64K。于是便查阅了很多资料吗,了解到管道容量分为pipe capacitypipe_buf。这两者的区别在于pipe_buf定义的是内核管道缓冲区的大小,这个值的大小是由内核设定的,这个值仅需一条命令就可以查到;而pipe capacity指的是管道的最大值,即容量,是内核内存中的一个缓冲区。

二、管道的概念

上线了解了linux中的管道的相关操作,接下来我们来学习一下管道的概念。

管道是UNIX系统上最古老的IPC方法,它在20世纪70年代早期UNIX的第三个版本上就出现了。把一个进程连接到另一个进程的数据流称为管道,管道被抽象成一个文件,称为管道文件(pipe)。

管道可以分为两种:无名(匿名)管道和有名管道。两者的特点如下:

无名管道 - 只能用于具有亲缘关系的进程之间的通信(父子进程,兄弟进程)。
- 是单工的通信模式,具有固定的读端和写端,只能一端读,一端写(程序实现设计好)。
- 无名管道创建时会返回两个文件描述符,分别用于读写管道。
- 管道可以用于多于2个进程共享。
有名管道 - 有名管道可以使非亲缘的两个进程互相通信
- 通过路径名来操作,在文件系统中可见,但内容存放在内存中;
- 文件IO来操作有名管道;
- 遵循先进先出规则;
- 不支持leek操作;
- 单工读写。

【注意事项】不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。另外他们都是单工读写的,若要实现双向的数据传输,可以使用两个管道。