c - fd 泄漏,自定义 Shell

标签 c linux shell unix

我正在开发一个可以处理多个管道的自定义 shell。但是每次我执行一个新的管道并使用 ls -l/proc/pid/fd 检查进程时,我都会得到如下图所示的内容,并且列表会随着每个新管道的执行而不断扩展:

screenshot of terminal

问题:这算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/

相关文章:

shell - 使用 emacs shell 时清除 shell 的命令

从 C 调用本地 Julia 包

c - 宏参数的名称可以是关键字吗?

缺点 功能不起作用

c - 整数到字符串转换器(使用宏)

linux - 如何在远程服务器上以 root 身份执行本地脚本?

linux - 使用 Emacs 编辑 GVFS 挂载文件

linux - bash getopts 验证选项

Linux:将输出变成一行

shell - 如何使用 MONO 运行带有参数的程序?