为什么要在 linux 中关闭管道?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19265191/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:02:38  来源:igfitidea点击:

Why should you close a pipe in linux?

clinuxpipe

提问by pghazanfari

When using a pipe for process-process communication, what is the purpose of closing one end of the pipe?

使用管道进行进程间通信时,关闭管道一端的目的是什么?

For example: How to send a simple string between two programs using pipes?

例如:如何使用管道在两个程序之间发送一个简单的字符串?

Notice that one side of the pipe is closed in the child and parent processes. Why is this required?

请注意,管道的一侧在子进程和父进程中是关闭的。为什么需要这样做?

回答by Joni

The number of file descriptors that can be open at a given time is limited. If you keep opening pipes and not closing them pretty soon you'll run out of FDs and can't open anything anymore: not pipes, not files, not sockets, ...

在给定时间可以打开的文件描述符的数量是有限的。如果你一直打开管道而不很快关闭它们,你会用完 FD 并且无法再打开任何东西:不是管道,不是文件,不是套接字,......

Another reason why it can be important to close the pipe is when the closing itself has a meaning to the application. For example, a common use of pipes is to send the errnofrom a child process to the parent when using forkand execto launch an external program:

关闭管道很重要的另一个原因是关闭本身对应用程序有意义。例如,管道的一个常见的用途是发errno用时从一个子进程的父forkexec启动一个外部程序

  1. The parent creates the pipe, calls forkto create a child process, closes its writing end, and tries to read from the pipe.
  2. The child process attempts to use execto run a different program:
    1. If execfails, for example because the program does not exist, the child writes errnoto the pipe, and the parent reads it and knows what went wrong, and can tell the user.
    2. If execis successful the pipe is closed without anything being written. The readfunction in the parent returns 0 indicating the pipe was closed and knows the program was successfully started.
  1. fork进程创建管道,调用创建子进程,关闭其写入端,并尝试从管道中读取。
  2. 子进程尝试使用exec来运行不同的程序:
    1. 如果exec失败,例如因为程序不存在,子进程写入errno管道,父进程读取它并知道哪里出了问题,并且可以告诉用户。
    2. 如果exec成功,管道将关闭而不写入任何内容。将read在指示管父返回0功能被关闭,并且知道该程序已成功启动。

If the parent did not close its writing end of the pipe before trying to read from the pipe this would not work because the readfunction would never return when execis successful.

如果父级在尝试从管道读取之前没有关闭管道的写入端,这将不起作用,因为该read函数在exec成功时永远不会返回。

回答by glglgl

If you connect two processes - parent and child - using a pipe, you create the pipe before the fork.

如果使用管道连接两个进程 - 父进程和子进程 - 则在 fork 之前创建管道。

The fork makes the both processes have access to both ends of the pipe. This is not desirable.

fork 使两个进程都可以访问管道的两端。这是不可取的。

The reading side is supposed to learn that the writer has finished if it notices an EOF condition. This can only happen if all writing sides are closed. So it is best if it closes its writing FD ASAP.

如果它注意到 EOF 条件,阅读方应该知道作者已经完成。只有当所有书写面都关闭时才会发生这种情况。所以最好尽快关闭它的写入FD。

The writer should close its reading FD just in order not to have too many FDs open and thus reaching a maybe existing limit of open FDs. Besides, if the then only reader dies, the writer gets notified about this by getting a SIGPIPE or at least an EPIPE error (depending on how signals are defined). If there are several readers, the writer cannot detect that "the real one" went away, goes on writing and gets stuck as the writing FD blocks in the hope, the "unused" reader will read something.

编写器应该关闭其读取 FD,以免打开太多 FD,从而达到可能存在的打开 FD 限制。此外,如果当时唯一的读取器死了,写入器会通过获取一个 SIGPIPE 或至少一个 EPIPE 错误(取决于信号的定义方式)来获得通知。如果有多个读者,作者无法检测到“真正的”消失了,继续写作并被卡住,希望“未使用”的读者会读到一些东西。

So here in detail what happens:

所以在这里详细说明会发生什么:

  • parent process calls pipe()and gets 2 file descriptors: let's call it rdand wr.
  • parent process calls fork(). Now both processes have a rdand a wr.
  • Suppose the child process is supposed to be the reader.

    Then

    • the parent should close its reading end (for not wasting FDs and for proper detection of dying reader) and
    • the child must close its writing end (in order to be possible to detect the EOF condition).
  • 父进程调用pipe()并获取 2 个文件描述符:让我们调用它rdwr.
  • 父进程调用fork(). 现在两个进程都有 ard和 a wr
  • 假设子进程应该是读者。

    然后

    • 父母应该关闭它的阅读端(为了不浪费 FD 和正确检测垂死的读者)和
    • 孩子必须关闭其写入端(以便可以检测到 EOF 条件)。

回答by Holeryn

Closing unused pipe file descriptor is more than a matter of ensuring that a process doesn't exhaust its limited set of file descriptor-it is essential to the correct use of pipes. We now consider why the unused file descriptors for both the read and write ends of the pipe must be closed. The process reading from the pipe closes its write descriptor for the pipe, so that, when the other process completes its output and closes its write descriptor, the read sees end-of-file (once it has ready any outstanding data in the pipe). If the reading process doesn't close the write end of the pipe, then after the other process closes its write descriptor, the reader won't see end-of-file, even after it has read all data from the pipe. Instead, a read()would block waiting for data, because the kernel knows that there is still at least one write descriptor open for the pipe.That this descriptor is held open by the reading process itself is irrelevant; In theory, that process could still write to the pipe, even if it is blocked trying to read. For example, the read()might be interrupted by a signal handler that writes data to the pipe. The writing process closes its read descriptor for the pipe for a different reason. When a process tries to write to a pipe for which no process has an open read descriptor, the kernel sends the SIGPIPEsignal to the writing process. By default, this signal kills a process. A process can instead arrange to catch or ignore this signal, in which case the write()on the pipe fails with the error EPIPE(broken pipe). Receiving the SIGPIPEsignal or getting the EPIPEerror is useful indication about the status of the pipe, and this is why unused read descriptors for the pipe should be closed. If the writing process doesn't close the read end of the pipe, then even after the other process closes the read end of the pipe, the writing process will fill the pipe, and a further attempt to write will block indefinitely. One final reason for closing unused file descriptor is that only after it all file descriptor are closed that the pipe is destroyed and its resources released for reuse by other processes. At this point, any unread data in the pipe is lost.

关闭未使用的管道文件描述符不仅仅是确保进程不会耗尽其有限的文件描述符集 - 这对于正确使用管道至关重要。我们现在考虑为什么必须关闭管道读写端未使用的文件描述符。从管道读取的进程关闭其管道的写入描述符,因此,当另一个进程完成其输出并关闭其写入描述符时,读取看到文件结束(一旦它准备好管道中的任何未完成的数据) . 如果读进程没有关闭管道的写端,那么在另一个进程关闭它的写描述符后,读取器将不会看到文件结束,即使它已经从管道中读取了所有数据。相反,一个read()会阻塞等待数据,因为内核知道仍然至少有一个写描述符为管道打开。这个描述符由读进程本身保持打开是无关紧要的;理论上,该进程仍然可以写入管道,即使它在尝试读取时被阻止。例如,read()可能会被将数据写入管道的信号处理程序中断。写入过程出于不同的原因关闭了管道的读取描述符。当一个进程试图写入一个没有进程打开读描述符的管道时,内核向SIGPIPE写进程发送信号。默认情况下,这个信号会杀死一个进程。一个进程可以安排捕获或忽略这个信号,在这种情况下write(),管道上的失败并出现错误EPIPE(破管)。接收SIGPIPE信号或获取EPIPE错误是关于管道状态的有用指示,这就是为什么应该关闭管道未使用的读取描述符。如果写进程没有关闭管道的读端,那么即使在另一个进程关闭了管道的读端之后,写进程也会填满管道,进一步的写尝试将无限期地阻塞。关闭未使用的文件描述符的最后一个原因是,只有在所有文件描述符都关闭后,管道才会被销毁并释放其资源以供其他进程重用。此时,管道中的任何未读数据都将丢失。

~ Micheal Kerrisk , the Linux programming interface

~ Micheal Kerrisk,Linux 编程接口