在 c 中处理管道时关闭文件描述符
Closing file descriptors when handling pipes in c
我正在尝试实现一个非常简单的 shell,它只处理管道,没有语法错误,所以我有一个这样声明的 AST:
typedef struct s_ast t_ast;
struct s_ast
{
int type;
t_ast *l;
t_ast *r;
char **cmd;
};
type
只能容纳 0 或 1(CMD 或 PIPE)。
如果type
等于PIPE,l
和r
是管道的左/右分支
cmd
是带参数的命令,如果 type
等于 CMD
然后我有我的 exe 函数,它只调用另外两个函数之一:exe_pipe
或 exe_cmd
。
exe_cmd
函数只是在子进程中调用 execve
并等待它结束。
现在,这是我的 exe_pipe
函数(没有对系统调用进行错误检查以提高可读性):
void exe_pipe(t_ast *ast, char **env)
{
int fd[2], pid[2];
pipe(fd);
if ((pid[0] = fork()) == 0)
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
exe(ast->l, env);
close(fd[1]);
exit(EXIT_FAILRE);
}
if ((pid[1] = fork()) == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
exe(ast->r, env);
close(fd[0]);
exit(EXIT_FAILURE);
}
waitpid(-1, NULL, 0);
close(fd[0]); close(fd[1]);
waitpid(-1, NULL, 0);
}
基本上,我分叉两次以创建 2 个“subshells”,每个都将执行其管道的一侧。
我的问题是这段代码泄露了一些文件描述符,但我不知道在哪里关闭每个文件描述符,我想我必须在某处使用 SIGPIPE
信号,但我对此不太满意.
你可以找到完整的代码here如果你想测试它
复制后需要关闭文件描述符。样板是:
pipe(fd);
...
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execl(....)
由于您在关闭管道之前正在执行 children,因此当您调用 exe
时打开的 fd 过多。特别是,第一个 child 在执行时仍然有 fd[1]
打开,并且由于该文件描述符仍然打开,第二个 child 永远不会在其标准输入上看到 EOF。 (我假设 exe
调用 exec*
。如果是这样,在 exe
之后调用 close
没有任何好处。)
总是计算你的fd。假设您从 3(0、1 和 2)开始。在调用pipe
之后,你有5个。在你dup2
之后,你还有5个。(一个被dup2
打开,一个被关闭。)你只想有3个当 child 执行时,您需要关闭 2.
我正在尝试实现一个非常简单的 shell,它只处理管道,没有语法错误,所以我有一个这样声明的 AST:
typedef struct s_ast t_ast;
struct s_ast
{
int type;
t_ast *l;
t_ast *r;
char **cmd;
};
type
只能容纳 0 或 1(CMD 或 PIPE)。
如果type
等于PIPE,l
和r
是管道的左/右分支
cmd
是带参数的命令,如果 type
等于 CMD
然后我有我的 exe 函数,它只调用另外两个函数之一:exe_pipe
或 exe_cmd
。
exe_cmd
函数只是在子进程中调用 execve
并等待它结束。
现在,这是我的 exe_pipe
函数(没有对系统调用进行错误检查以提高可读性):
void exe_pipe(t_ast *ast, char **env)
{
int fd[2], pid[2];
pipe(fd);
if ((pid[0] = fork()) == 0)
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
exe(ast->l, env);
close(fd[1]);
exit(EXIT_FAILRE);
}
if ((pid[1] = fork()) == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
exe(ast->r, env);
close(fd[0]);
exit(EXIT_FAILURE);
}
waitpid(-1, NULL, 0);
close(fd[0]); close(fd[1]);
waitpid(-1, NULL, 0);
}
基本上,我分叉两次以创建 2 个“subshells”,每个都将执行其管道的一侧。
我的问题是这段代码泄露了一些文件描述符,但我不知道在哪里关闭每个文件描述符,我想我必须在某处使用 SIGPIPE
信号,但我对此不太满意.
你可以找到完整的代码here如果你想测试它
复制后需要关闭文件描述符。样板是:
pipe(fd);
...
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execl(....)
由于您在关闭管道之前正在执行 children,因此当您调用 exe
时打开的 fd 过多。特别是,第一个 child 在执行时仍然有 fd[1]
打开,并且由于该文件描述符仍然打开,第二个 child 永远不会在其标准输入上看到 EOF。 (我假设 exe
调用 exec*
。如果是这样,在 exe
之后调用 close
没有任何好处。)
总是计算你的fd。假设您从 3(0、1 和 2)开始。在调用pipe
之后,你有5个。在你dup2
之后,你还有5个。(一个被dup2
打开,一个被关闭。)你只想有3个当 child 执行时,您需要关闭 2.