分叉后关闭管道文件描述符时出现错误的文件描述符错误

Bad file descriptor error when closing pipe file descriptors after forking

我尝试让进程的 2 个子进程进行通信:

void demo() {
    int pipes[2];
    int create_pipe_status = pipe(pipes);
    int status;
    pid_t fork_id = fork();
    if(!fork_id) {
        //parent

        status = dup2(pipes[1], STDOUT_FILENO);
        close(pipes[0]);
        close(pipes[1]);
    }

    pid_t fork_id2 = fork();
    if(!fork_id2){
        //child
        status = dup2(pipes[0], STDIN_FILENO);
        close(pipes[0]);
        close(pipes[1]); //keep
    }
    wait(NULL);
    wait(NULL);
}

当我尝试关闭与管道关联的文件描述符时,我在 errno 中收到错误 EBADF 错误的文件描述符)。

我的错误是什么?

首先,现有进程调用 fork(),然后这个原始进程及其派生的子进程再次调用 fork(),总共有四个进程。即进程为:

  1. 原来的流程。
  2. 原始进程的第一个子进程 - 由原始进程第一次调用 fork() 创建。
  3. 原始进程的第二个子进程 - 由原始进程第二次调用 fork() 创建。
  4. 子进程的子进程-由子进程第二次调用fork()创建。

因此,代码块(即第二个if块)被子进程的子进程执行:

if(!fork_id2){
    //child
    status = dup2(pipes[0], STDIN_FILENO);
    close(pipes[0]);
    close(pipes[1]);
}

子进程的这个子进程试图关闭pipe[0]pipe[1]。但是,子进程(即子进程的子进程的父进程)已经关闭了pipe[0]pipe[1]。这就是坏文件描述符错误的来源,即关闭文件描述符两次。

请注意,即使原始进程的第二个子进程也执行此代码块,但它不会遇到此问题,因为原始进程在调用时尚未关闭管道文件描述符fork() 第二次,所以当原始进程的第二个子进程关闭它们时,两个文件描述符仍然打开。


我猜你真正想要的是两个从现有进程创建两个子进程。如果是这样,那么只需将对 fork() 的第二次调用限制为父进程(即原始进程):

void demo() {
    int pipes[2];
    int create_pipe_status = pipe(pipes);
    int status;

    pid_t fork_id = fork();
    if (!fork_id) { // 1st child
        status = dup2(pipes[1], STDOUT_FILENO);
        assert(!close(pipes[0]));
        assert(!close(pipes[1]));
    } else {
      // parent
      pid_t fork_id2 = fork();
      if (!fork_id2) { // 2nd child
        status = dup2(pipes[0], STDIN_FILENO);
        assert(!close(pipes[0]));
        assert(!close(pipes[1]));
      } else { // parent
        assert(!close(pipes[0]));
        assert(!close(pipes[1]));
      }
    }
    wait(NULL);
    wait(NULL);
}