我正在开发一个可以处理多个管道的自定义 shell。但是每次我执行一个新的管道并使用 ls -l/proc/pid/fd
检查进程时,我都会得到如下图所示的内容,并且列表会随着每个新管道的执行而不断扩展:
问题:这算fd泄漏吗?我该如何解决?
这是我的管道执行的代码片段:
enum PIPES {READ, WRITE};
void execute_pipeline(char*** pipeline)
{
int fd[2];
int fd_backup = 0;
pid_t child_pid;
while (*pipeline != '\0')
{
pipe(fd);
child_pid = fork();
if(child_pid == -1)
{
perror("fork");
exit(1);
}
else if(child_pid == 0)
{
dup2(fd_backup, 0);// (old, new)
close(fd[READ]);
if(*(pipeline + 1) != '\0')
{
dup2(fd[WRITE], 1);
}
execvp((*pipeline)[0], *pipeline);
exit(1);
}
else// Parent process
{
wait(NULL);
close(fd[WRITE]);
fd_backup = fd[READ];
pipeline++;
}
}
}
编辑
如何调用 execute_pipeline 的示例:
char *ls[] = {"ls", "-l", NULL};
char *sort[] = {"sort", "-r", NULL};
char *head[] = {"head", "-n", "3", NULL};
char **pipeline[] = {ls, sort, head, NULL};
execute_pipeline(pipeline);
最佳答案
让我们准确了解文件描述符,并记住在 fork 和 execvp 期间,文件描述符由子进程继承,除非标记为带有 CLOEXEcflags。查看手册页:
fork (2): 子进程继承父进程的打开文件描述符集的副本。子项中的每个文件描述符都引用与父项中相应文件描述符相同的打开文件描述(请参阅 open(2))。
打开(2): 默认情况下,新文件描述符设置为在 execve(2) 中保持打开状态(即,fcntl(2) 中描述的 FD_CLOEXEC 文件描述符标志最初被禁用); O_CLOEXEcflags,如下所述,可用于更改此默认值。文件偏移量设置为文件的开头(请参阅 lseek(2))。
但是我想,这种行为正是您通过在管道后调用 fork 所依赖的...
废话不多说,让我们来画这个:
stdin, stdout
/\
/ \
/ \
/ \
/ R; W; \
/ \
Child - - Parent
stdin/out, R(del),W stdin/out, R(fd_backup), W(del)
/\
/ \
/ \
/ \
/ R1; W1;\
/ \
Child - - Parent
stdin/out, R(fd_backup), stdin, stdout
R1(del), W1 R(fd_backup - old);
R1(fd_backup - new); W1(del)
/ \
/ \
/ \
/R2; W2;\
/ \
/ \
Child - - Parent
stdin, stdout, stdin, stdout
R(fd_backup - old), R (fd_backup - old),
R1(fd_backup - new), R1 (fd_backup - new),
R2(del),W2 R2 (fd_backup - newest!),
我希望图片是不言自明的。
子进程无论如何都会死掉,它们所有的 fds 都会被关闭(所以它们没有问题)。但是父进程留下了 3 个打开的 fds,并且它们随着每个管道的执行而不断增长。
关于c - fd 泄漏,自定义 Shell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52823093/