为什么使用命名管道而不是文件?

use*_*885 51 files pipe

我最近阅读了命名管道,但我不明白它们为什么存在。
我在某处读到使用命名管道比使用文件更省时。

为什么会这样?
命名管道也必须存储在内存中(可能会被交换,就像文件一样)。
据我所知,他们必须获得一个必须由当前目录引用的 inode,就像文件一样。此外,它们必须由程序员删除,就像文件一样。

那么优势在哪里呢?

c0r*_*0rp 56

Linux 中的几乎所有东西都可以被视为文件,但常规文件命名管道之间的主要区别在于,命名管道是文件系统上没有内容的文件的特殊实例。

这是引自man fifo

FIFO 特殊文件(命名管道)类似于管道,不同之处在于它作为文件系统的一部分进行访问。它可以被多个进程打开以进行读取或写入。当进程通过 FIFO 交换数据时,内核在内部传递所有数据而不将其写入文件系统。因此,FIFO 特殊文件在文件系统上没有内容;文件系统条目仅用作参考点,以便进程可以使用文件系统中的名称访问管道。

内核为至少一个进程打开的每个 FIFO 特殊文件维护一个管道对象。FIFO 必须在两端(读和写)都打开,然后才能传递数据。通常,打开 FIFO 会阻塞,直到另一端也打开。

所以实际上一个命名管道在某些进程读写它之前什么都不做。它不占用硬盘上的任何空间(除了一点元信息),它不使用 CPU。

您可以通过执行以下操作来检查它:

创建命名管道

$ mkfifo /tmp/testpipe
Run Code Online (Sandbox Code Playgroud)

转到某个目录,例如/home/user/Documents,使用命名管道 gzip 里面的所有内容。

$ cd /home/user/Documents
$ tar cvf - . | gzip > /tmp/testpipe &
[1] 28584
Run Code Online (Sandbox Code Playgroud)

在这里您应该会看到 gzip 进程的 PID。在我们的示例中,它是 28584。

现在检查这个PID在做什么

$ ps u -P 28584
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
c0rp     28584  0.0  0.0  29276  7800 pts/8    S    00:08   0:00 bash
Run Code Online (Sandbox Code Playgroud)

您将看到它不使用任何资源。CPU 使用率 0%,内存使用率 0%。

验证有关文件空间使用情况的预感

$ du -h /tmp/testpipe
0   testpipe
Run Code Online (Sandbox Code Playgroud)

再一次0,什么都没有。如果需要,可以再次使用试管。

不要忘记使用kill -15 28584. 并使用删除我们的命名管道rm /tmp/testpipe

示例用法

您可以使用命名管道重定向几乎所有内容。例如,您可以看到这一行代理

另外这里是一个更命名管道的使用很好的解释。您可以将一台服务器上的两个进程配置为使用命名管道而不是 TCP/IP 堆栈进行通信。它更快,并且不会加载网络资源。例如,您的 Web 服务器可以使用命名管道直接与数据库通信,而不是使用localhost地址或侦听某个端口。

  • 嗯,不。Gzip 并没有在 no-cpu 中神奇地运行,它根本没有运行——它被阻止等待读取。 (3认同)

GAR*_*vid 19

确实,您不会使用系统内存,但您在示例中不使用 cpu 的事实只是因为您没有读取管道,因此进程正在等待。

考虑以下示例:

mkfifo /tmp/testpipe
tar cvf - / | gzip > /tmp/testpipe
Run Code Online (Sandbox Code Playgroud)

现在打开一个新的控制台并运行:

watch -n 1 'ps u -P $(pidof tar)
Run Code Online (Sandbox Code Playgroud)

在第三个控制台中:

cat /tmp/testpipe > /dev/null
Run Code Online (Sandbox Code Playgroud)

如果您查看 watch cmd(第二项),它将显示 CPU 消耗增加!

  • 这个答案是关于 [c0rp 的答案](https://askubuntu.com/a/449192/301745) (2认同)

Zak*_*har 8

这是一个用例,其中命名管道可以通过删除 I/O 为您节省大量时间。

假设您有一个 BigFile,例如 10G。

您还可以将此 BigFile 拆分为 1G,BigFileSplit_01 到 BigFile_Split_10。

现在你对 BigFileSplit_05 的正确性有疑问

天真地,如果没有命名管道,您将从 BigFile 创建一个新的拆分并进行比较:

dd if=BigFile of=BigFileSplitOrig_05 bs=1G skip=4 count=1
diff -s BigFileSplitOrig_05 BigFileSplit_05
rm BigFileSplitOrig_05
Run Code Online (Sandbox Code Playgroud)

使用命名管道你会做

mkfifo BigFileSplitOrig_05
dd if=BigFile of=BigFileSplitOrig_05 bs=1G skip=4 count=1 &
diff -s BigFileSplitOrig_05 BigFileSplit_05
rm BigFileSplitOrig_05
Run Code Online (Sandbox Code Playgroud)

乍一看,这似乎没有太大区别……但随着时间的推移,差异是巨大的!

选项1:

  • dd:读1G/写1G (1)
  • 差异:读取2G
  • rm:释放分配的集群/删除目录条目

选项 2:

  • dd:没什么!(转到命名管道)
  • 差异:读取2G
  • rm:没有分配的集群来管理(我们实际上没有向文件系统写入任何内容)/删除目录条目

所以基本上命名管道在这里为您节省了 1G 的读写以及一些文件系统清理(因为我们没有向文件系统写入任何内容,而是空的 fifo 节点)。

不进行 I/O,尤其是写入,也有助于避免磁盘磨损。使用 SSD 时更有趣,因为它们在单元死亡之前的写入次数有限。

(1) 显然,另一种选择是将临时文件创建到 RAM,例如,如果 /tmp 挂载到 RAM (tmpfs)。然而,您会受到 RAM 磁盘大小的限制,而“命名管道技巧”则没有限制。

  • 一点也不!这正是它节省 I/O 的原因。标准管道缓冲区为 1Mb(请参阅 /proc/sys/fs/pipe-max-size),因此第一个 dd 命令将将该数量写入管道,该管道纯粹在内存中,并阻塞直到另一个进程从管道读取并为更多数据释放一些空间。“另一个进程”是 diff 命令,它将“消耗”管道中的数据。所以不,完全不是,这正是管道的目的,写入 fifo 不会写入磁盘......除非你交换到管道本身需要交换的点! (3认同)