【问题标题】:Pipe() with fork() with recursion: File Descriptors handlingPipe() with fork() with recursion:文件描述符处理
【发布时间】:2014-02-15 11:09:15
【问题描述】:

我对昨天提出的一个现有问题感到困惑:
Recursive piping in Unix again

我重新发布有问题的代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void pipeline( char * ar[], int pos, int in_fd);
void error_exit(const char*);
static int child = 0; /* whether it is a child process relative to main() */

int main(int argc, char * argv[]) {
    if(argc < 2){
        printf("Usage: %s option (option) ...\n", argv[0]);
        exit(1);
    }
    pipeline(argv, 1, STDIN_FILENO);
    return 0;
}

void error_exit(const char *kom){
    perror(kom);
    (child ? _exit : exit)(EXIT_FAILURE);
}

void pipeline(char *ar[], int pos, int in_fd){
    if(ar[pos+1] == NULL){ /*last command */
        if(in_fd != STDIN_FILENO){
            if(dup2(in_fd, STDIN_FILENO) != -1)
                close(in_fd); /*successfully redirected*/
            else error_exit("dup2");
        }
        execlp(ar[pos], ar[pos], NULL);
        error_exit("execlp last");
    }
    else{
        int fd[2];
        pid_t childpid;

        if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {
            error_exit("Failed to setup pipeline");
        }
        if (childpid == 0){ /* child executes current command */
            child = 1;
            close(fd[0]);
            if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */
                perror("Failed to redirect stdin");
            if (dup2(fd[1], STDOUT_FILENO) == -1)   /*write to fd[1]*/
                perror("Failed to redirect stdout");
            else if ((close(fd[1]) == -1) || (close(in_fd) == - 1))
                perror("Failed to close extra pipe descriptors");
            else {
                execlp(ar[pos], ar[pos], NULL);
                error_exit("Failed to execlp");
            }
        }
        close(fd[1]);   /* parent executes the rest of commands */
        close(in_fd);
        pipeline(ar, pos+1, fd[0]);
    }
}

发生的错误是:

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor

建议的解决方案是,“不要在子进程中关闭文件描述符 fd[1] 和 in_fd,因为它们已经在父进程中关闭。”

我的困惑:(对不起,我是 Linux 新手)
根据我的《开始 Linux 编程》一书,当我们 fork() 一个进程时,文件描述符也会被复制。因此父母和孩子应该有不同的文件描述符。这与答案相矛盾。

我的尝试:
我尝试自己运行这段代码,我发现问题只有在我关闭两个进程(父进程和子进程)中的“in_fd”文件描述符时才会出现。它不依赖于 fd[1]。
另外,有趣的是,如果我尝试./prog ls sort head,它可以正常工作,但是当我尝试./prog ls sort head uniq 时,它会在head 上出现读取错误。

我的想法: in_fd 文件描述符只是此函数的输入 int 变量。似乎即使在 fork 之后,也只剩下一个文件描述符被父子共享。但我无法理解。

【问题讨论】:

  • 请看我的回答,如果我无法澄清任何事情,请在此处发表评论。
  • dup2( in_fd, STDIN_FILENO) 在 in_fd == STDIN_FILENO 时有问题。您在代码中的某个地方发现了这个问题,但在另一个地方忽略了它。
  • @WilliamPursell 你能解释一下如果 in_fd 最初是 STDIN_FILENO 会导致什么问题吗?谢谢!
  • @user3154219 我错了。如果两个文件描述符相同,dup3 将失败,但对于 dup2:“如果 fildes2 已经是一个有效的打开文件描述符,则应首先关闭它,除非 fildes 等于 fildes2 在这种情况下 dup2() 应返回 fildes2 而不关闭它”,所以这应该不是问题。

标签: c linux unix


【解决方案1】:

当我们 fork() 一个进程时,文件描述符也是 重复。因此父母和孩子应该有不同的文件 描述符

文件描述符是一个简单的integer。所以当它被复制时,它具有相同的值,所以它们指向同一个文件。

因此您可以在父级中打开文件,并从子级访问它。唯一可能发生的问题是,如果文件是从父级和子级都访问的,在这种情况下,不能保证它将从文件的哪个位置访问。为避免这种情况,建议关闭 child 中的 fd 并重新打开。

正如你所说的那样,我对上述问题做了同样的事情,发现第四个命令总是发生这种情况。此外,dup2() 会关闭它正在复制的文件。在问题中,fd[1]in_fd 被复制到孩子的 stdinstdoutfd[1]in_fd 在那一刻关闭。无需再次关闭它们。

关闭一个已经关闭的描述符会导致错误。

并且由于您不知道先执行父级还是子级,如果您从子级关闭一个文件,然后再次从父级关闭,可能会导致问题,并且这种类型的行为是不可预测的。

【讨论】:

  • 关闭一个已经关闭的描述符可能未定义,我不确定。来自man 2 close:“close() 成功返回零。出错时返回-1,并且 errno 设置得当。错误 - EBADF fd 不是有效的打开文件描述符。始终检查您的返回代码,在这种情况下,errno
  • dup 与 dup2 不同吗?在我的书代码中,作者在调用 dup() 后显式关闭文件描述符。 dup(file_pipes[0]);关闭(file_pipes[0]);
  • @Evert,好的,所以关闭一个已经关闭的文件会给出EBADF,这里就是这种情况,但我不确定为什么它没有为其他文件描述符提供相同的值。
  • @user3154219,是的 dupdup2 是不同的,显然第二个需要 2 个参数。
  • @Dipto,是的,我的意思是 dup() 不会关闭调用文件描述符。作者明确关闭它。 dup2 会关闭吗?
猜你喜欢
  • 1970-01-01
  • 2019-09-06
  • 2015-09-14
  • 1970-01-01
  • 2012-10-16
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
相关资源
最近更新 更多