首页 文章

带有递归的fork()的Pipe():文件描述符处理

提问于
浏览
2

我对昨天提出的现有问题感到困惑:
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,因为它们已经在父进程中关闭了 . ”

My Confusion: (sorry I am a newbie in Linux)
根据我的书"Beginning Linux Programming",当我们fork()一个进程时,文件描述符也是重复的 . 因此父母和孩子应该有不同的文件描述符 . 这与答案相矛盾 .

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

My Thoughts: in_fd 文件描述符只是此函数的输入int变量 . 似乎即使在fork之后,只有一个文件描述符仍然由父和子共享 . 但我无法理解如何 .

1 回答

  • 0

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

    file descriptor is a simple integer. so when it is copied, it has same value so they point to same file.

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

    正如你已经说明了你的尝试,我对上述问题做了同样的事情,发现总是发生第4次命令 . 此外, dup2() 关闭它正在复制的文件 . 在这个问题中, fd[1]in_fd 被复制到了孩子的 stdinstdout . 并且 fd[1]in_fd 在那一刻被关闭了 . 没有必要再次关闭它们 .

    关闭已关闭的描述符将导致错误 .

    并且由于您不知道父项或子项是否将首先执行,如果您从子项关闭一个文件,并再次从父项关闭,可能会导致问题,并且此类行为是不可预测的 .

相关问题