为什么我们需要在 execvp 之前关闭管道?
Why do we need to call close on pipes before execvp?
我一直在尝试在应用程序中使用管道实现类似 shell 的功能,并且我正在关注 this example。如果原始代码被删除,我将在此处复制代码以供将来参考:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* Executes the command "cat scores | grep Villanova | cut -b 1-10".
* This quick-and-dirty version does no error checking.
*
* @author Jim Glenn
* @version 0.1 10/4/2004
*/
int main(int argc, char **argv)
{
int status;
int i;
// arguments for commands; your parser would be responsible for
// setting up arrays like these
char *cat_args[] = {"cat", "scores", NULL};
char *grep_args[] = {"grep", "Villanova", NULL};
char *cut_args[] = {"cut", "-b", "1-10", NULL};
// make 2 pipes (cat to grep and grep to cut); each has 2 fds
int pipes[4];
pipe(pipes); // sets up 1st pipe
pipe(pipes + 2); // sets up 2nd pipe
// we now have 4 fds:
// pipes[0] = read end of cat->grep pipe (read by grep)
// pipes[1] = write end of cat->grep pipe (written by cat)
// pipes[2] = read end of grep->cut pipe (read by cut)
// pipes[3] = write end of grep->cut pipe (written by grep)
// Note that the code in each if is basically identical, so you
// could set up a loop to handle it. The differences are in the
// indicies into pipes used for the dup2 system call
// and that the 1st and last only deal with the end of one pipe.
// fork the first child (to execute cat)
if (fork() == 0)
{
// replace cat's stdout with write part of 1st pipe
dup2(pipes[1], 1);
// close all pipes (very important!); end we're using was safely copied
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cat_args, cat_args);
}
else
{
// fork second child (to execute grep)
if (fork() == 0)
{
// replace grep's stdin with read end of 1st pipe
dup2(pipes[0], 0);
// replace grep's stdout with write end of 2nd pipe
dup2(pipes[3], 1);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*grep_args, grep_args);
}
else
{
// fork third child (to execute cut)
if (fork() == 0)
{
// replace cut's stdin with input read of 2nd pipe
dup2(pipes[2], 0);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cut_args, cut_args);
}
}
}
// only the parent gets here and waits for 3 children to finish
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
for (i = 0; i < 3; i++)
wait(&status);
}
我无法理解为什么在调用 execvp
和读取或写入任何数据之前关闭管道。我相信这与将 EOF 标志传递给进程有关,以便它们可以停止读写,但是在将任何实际数据推送到管道之前我看不出这有什么帮助。我会很感激一个明确的解释。谢谢
I have trouble understanding why the pipes are being closed just before calling execvp and reading or writing any data.
管道没有关闭。相反,一些与管道末端关联的文件描述符正在关闭。每个子进程都将管道端文件描述符复制到它的一个或两个标准流上,然后关闭它实际上不会使用的所有管道端文件描述符,这些文件描述符存储在 pipes
大批。只要每一端在至少一个进程中打开,每个管道本身就保持打开和可用状态,并且每个子进程至少保持一个管道的一端打开。当子进程终止时(或至少在子进程的控制下,post execvp()
),它们将关闭。
执行此类关闭的一个原因是为了整洁和资源管理。一个进程一次可以打开多少个文件描述符是有限制的,所以避免打开不需要的文件描述符是明智的。
而且,在功能上,从其中一个管道读取的进程将不会检测到文件结尾,直到 所有 打开与管道写入端关联的文件描述符,在任何情况下进程,关闭。这就是 EOF on a pipe 的意思,这是有道理的,因为只要写端在任何地方打开,就有可能向其写入更多数据。
我一直在尝试在应用程序中使用管道实现类似 shell 的功能,并且我正在关注 this example。如果原始代码被删除,我将在此处复制代码以供将来参考:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* Executes the command "cat scores | grep Villanova | cut -b 1-10".
* This quick-and-dirty version does no error checking.
*
* @author Jim Glenn
* @version 0.1 10/4/2004
*/
int main(int argc, char **argv)
{
int status;
int i;
// arguments for commands; your parser would be responsible for
// setting up arrays like these
char *cat_args[] = {"cat", "scores", NULL};
char *grep_args[] = {"grep", "Villanova", NULL};
char *cut_args[] = {"cut", "-b", "1-10", NULL};
// make 2 pipes (cat to grep and grep to cut); each has 2 fds
int pipes[4];
pipe(pipes); // sets up 1st pipe
pipe(pipes + 2); // sets up 2nd pipe
// we now have 4 fds:
// pipes[0] = read end of cat->grep pipe (read by grep)
// pipes[1] = write end of cat->grep pipe (written by cat)
// pipes[2] = read end of grep->cut pipe (read by cut)
// pipes[3] = write end of grep->cut pipe (written by grep)
// Note that the code in each if is basically identical, so you
// could set up a loop to handle it. The differences are in the
// indicies into pipes used for the dup2 system call
// and that the 1st and last only deal with the end of one pipe.
// fork the first child (to execute cat)
if (fork() == 0)
{
// replace cat's stdout with write part of 1st pipe
dup2(pipes[1], 1);
// close all pipes (very important!); end we're using was safely copied
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cat_args, cat_args);
}
else
{
// fork second child (to execute grep)
if (fork() == 0)
{
// replace grep's stdin with read end of 1st pipe
dup2(pipes[0], 0);
// replace grep's stdout with write end of 2nd pipe
dup2(pipes[3], 1);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*grep_args, grep_args);
}
else
{
// fork third child (to execute cut)
if (fork() == 0)
{
// replace cut's stdin with input read of 2nd pipe
dup2(pipes[2], 0);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cut_args, cut_args);
}
}
}
// only the parent gets here and waits for 3 children to finish
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
for (i = 0; i < 3; i++)
wait(&status);
}
我无法理解为什么在调用 execvp
和读取或写入任何数据之前关闭管道。我相信这与将 EOF 标志传递给进程有关,以便它们可以停止读写,但是在将任何实际数据推送到管道之前我看不出这有什么帮助。我会很感激一个明确的解释。谢谢
I have trouble understanding why the pipes are being closed just before calling execvp and reading or writing any data.
管道没有关闭。相反,一些与管道末端关联的文件描述符正在关闭。每个子进程都将管道端文件描述符复制到它的一个或两个标准流上,然后关闭它实际上不会使用的所有管道端文件描述符,这些文件描述符存储在 pipes
大批。只要每一端在至少一个进程中打开,每个管道本身就保持打开和可用状态,并且每个子进程至少保持一个管道的一端打开。当子进程终止时(或至少在子进程的控制下,post execvp()
),它们将关闭。
执行此类关闭的一个原因是为了整洁和资源管理。一个进程一次可以打开多少个文件描述符是有限制的,所以避免打开不需要的文件描述符是明智的。
而且,在功能上,从其中一个管道读取的进程将不会检测到文件结尾,直到 所有 打开与管道写入端关联的文件描述符,在任何情况下进程,关闭。这就是 EOF on a pipe 的意思,这是有道理的,因为只要写端在任何地方打开,就有可能向其写入更多数据。