LV005-管道简介
本文主要是进程通信——管道的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
一、Linux 管道
1. 无名管道
我们前边在学习Linux命令的时候,有学习过这么一个符号|,这就是管道,例如,
ps -elf | grep a.out它的功能是将前一个命令(ps -elf)的输出,作为后一个命令(grep a.out)的输入,不难看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行。上面这种管道是没有名字,所以|表示的管道称为匿名管道,也可以叫无名管道,用完了就销毁。
2. 有名管道
管道还有另外一个类型是命名管道,也被叫做 FIFO,也可以叫有名管道。因为数据是先进先出的传输方式。
2.1 创建有名管道
2.1.1 命令说明
在使用命名管道前,先需要通过 mkfifo 命令来创建,并且指定管道名字,可以在终端输入以下命令:
mkfifo <管道名称>2.1.2 使用实例
我们可以执行以下命令:
mkfifo myFifo要是在Windows共享文件夹目录(/mnt/hgfs/Sharedfiles)下创建的话可能会收到如下提示:
mkfifo: 无法创建先进先出文件'myFifo': 没有那个文件或目录归根结底是因为用的是共享文件夹,而Windows的文件系统又不支持管道文件。创建的管道文件路径必须设为linux的本地文件夹。所以我们不要在共享目录下创建就可以啦,创建完成后我们使用ls -alh查看一下创建的文件详细信息:
总用量 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 命令说明
- 向管道写入数据
echo "data" > 已创建的管道名称 # 可以包含路径- 从管道读取数据
cat < 已创建的管道名称 # 可以包含路径【注意事项】我们必须在管道所在的目录下执行上边的读写命令,或者就是指明创建的管道的路径。
2.2.2 使用实例
下边的演示,在管道文件myFifo所在的目录中进行。我们在终端执行以下命令:
echo "fanhua" > myFifo输入命令后,按下回车,我们会发现终端停住了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出。我们开启另一个终端,输入以下命令:
cat < myFifo然后,终端会有以下数据输出:
fanhua这个时候,我们会发现,写入数据的终端命令正常退出了,管道中的数据已经读取了,我们再读会出现什么情况?管道中没有数据的时候再执行一次读取命令,那么这个终端就会停住,直到另一个终端向管道写入数据,便会直接读取数据然后退出。
我们可以看出,管道这种通信方式效率低,不适合进程间频繁地交换数据。当然,它的好处就是简单,同时也我们很容易得知管道里的数据已经被另一个进程读取了。
3. 管道大小查看
那么既然管道是一种文件,它的最大大小是多少呢?我们可以使用如下命令查看:
ulimit -a然后终端会有如下信息提示:
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手册的时候,使用如下命令:
man 7 pipe然后找到Pipe capacity部分,有如下说明:
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 capacity和 pipe_buf。这两者的区别在于pipe_buf定义的是内核管道缓冲区的大小,这个值的大小是由内核设定的,这个值仅需一条命令就可以查到;而pipe capacity指的是管道的最大值,即容量,是内核内存中的一个缓冲区。
二、管道的概念
上线了解了linux中的管道的相关操作,接下来我们来学习一下管道的概念。
管道是UNIX系统上最古老的IPC方法,它在20世纪70年代早期UNIX的第三个版本上就出现了。把一个进程连接到另一个进程的数据流称为管道,管道被抽象成一个文件,称为管道文件(pipe)。
管道可以分为两种:无名(匿名)管道和有名管道。两者的特点如下:
| 无名管道 | - 只能用于具有亲缘关系的进程之间的通信(父子进程,兄弟进程)。 - 是单工的通信模式,具有固定的读端和写端,只能一端读,一端写(程序实现设计好)。 - 无名管道创建时会返回两个文件描述符,分别用于读写管道。 - 管道可以用于多于2个进程共享。 |
| 有名管道 | - 有名管道可以使非亲缘的两个进程互相通信; - 通过路径名来操作,在文件系统中可见,但内容存放在内存中; - 文件IO来操作有名管道; - 遵循先进先出规则; - 不支持leek操作; - 单工读写。 |
【注意事项】不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。另外他们都是单工读写的,若要实现双向的数据传输,可以使用两个管道。