C++ 了解dup2和关闭文件描述符

C++ 了解dup2和关闭文件描述符,c++,linux,dup2,C++,Linux,Dup2,我发布代码只是为了我的问题的上下文。我并没有明确地寻找你来帮助修复它,我更希望了解dup2系统调用,我只是没有从手册页和其他许多stackoverflow问题中学习 pid = fork(); if(pid == 0) { if(strcmp("STDOUT", outfile)) { if (command->getOutputFD() == REDIRECT) { if ((outfd = ope

我发布代码只是为了我的问题的上下文。我并没有明确地寻找你来帮助修复它,我更希望了解dup2系统调用,我只是没有从手册页和其他许多stackoverflow问题中学习

    pid = fork();

    if(pid == 0) {
        if(strcmp("STDOUT", outfile)) {
            if (command->getOutputFD() == REDIRECT) {
                if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
                    return false;
                command->setOutputFD(outfd);
                if (dup2(command->getOutputFD(), STDOUT_FILENO) == -1)
                    return false;
                pipeIndex++;
            }
            else if (command->getOutputFD() == REDIRECTAPPEND) {
                if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_APPEND)) == -1)
                    return false;
                command->setOutputFD(outfd);
                if (dup2(command->getOutputFD(), STDOUT_FILENO) == -1)
                    return false;
                pipeIndex++;
            }
            else {
                if (dup2(pipefd[++pipeIndex], STDOUT_FILENO) == -1)
                    return false;
                command->setOutputFD(pipefd[pipeIndex]);
            }
        }

        if(strcmp("STDIN", infile)) {
            if(dup2(pipefd[pipeIndex - 1], STDIN_FILENO) == -1)
                return false;
            command->setOutputFD(pipefd[pipeIndex - 1]);
            pipeIndex++;
        }


        if (execvp(arguments[0], arguments) == -1) {
            std::cerr << "Error!" << std::endl;
            _Exit(0);
        }

    }

    else if(pid == -1) {
        return false;
    }
ls-l>output.txt的输出意味着我关闭了错误的描述符,但是关闭了其他相关的描述符,在没有呈现错误的情况下,不会向文件提供任何输出。如
ls-l
所示,
grep“cook”
应向控制台生成输出

使用用于处理管道的文件描述符数组 有所有这些描述符的副本。描述词是什么时候掌握的 家长关门了?更重要的是,哪些描述符?这是全部吗 他们?执行命令时未使用的所有命令

文件描述符可以通过以下三种方式之一关闭:

  • 您可以显式地对其调用
    close()
  • 进程终止,操作系统会自动关闭所有仍然打开的文件描述符
  • 当进程调用七个
    exec()
    函数之一并且文件描述符具有
    O_CLOEXEC
    标志时
  • 如您所见,大多数情况下,文件描述符将保持打开状态,直到您手动关闭它们。代码中也会发生这种情况-因为您没有指定
    O\u CLOEXEC
    ,所以当子进程调用
    execvp()
    时,文件描述符不会关闭。在子对象中,它们在子对象终止后关闭。父母也是如此。如果您希望在终止之前的任何时间发生这种情况,则必须手动调用
    close()

    在子系统中处理管道时,会留下哪些描述符 由哪些进程打开?假设我执行命令:ls-l | grep “[username]”,哪些描述符应为ls保留打开状态 过程就在管道的末端?如果是,什么时候?相同的 问题适用于grep命令

    这里有一个(粗略的)想法,当您键入
    ls-l | grep“username”
    时,shell会做什么:

  • shell调用
    pipe()
    来创建新管道。管道文件描述符将在下一步由子级继承
  • shell分叉两次,让我们调用这些进程
    c1
    c2
    。我们假设
    c1
    将运行
    ls
    c2
    将运行
    grep
  • c1
    中,管道的读取通道用
    close()
    关闭,然后它用管道写入通道和
    STDOUT\u FILENO
    调用
    dup2()
    ,从而使写入
    STDOUT
    等同于写入管道。然后,调用七个
    exec()
    函数中的一个开始执行
    ls
    ls
    写入
    stdout
    ,但由于我们将
    stdout
    复制到管道的写入通道,
    ls
    将写入管道
  • c2
    中,情况正好相反:管道的写入通道关闭,然后调用
    dup2()
    使
    stdin
    指向管道的读取通道。然后,调用七个
    exec()
    函数中的一个开始执行
    grep
    grep
    stdin
    读取,但由于我们
    dup2()
    将标准输入到管道的读取通道,
    grep
    将从管道读取
  • 当我处理IO重定向到文件时,必须打开一个新文件 并复制到标准输出(我不支持输入重定向)。什么时候 这个描述符关闭了吗?我在例子中看到它被关闭了 在调用dup2之后,但是接下来会发生什么呢 如果文件已关闭,是否写入文件

    因此,当您调用
    dup2(a,b)
    时,以下任一项均为真:

    • a==b
      。在这种情况下,不会发生任何事情,
      dup2()
      过早返回。没有关闭任何文件描述符
    • a!=b
      。在这种情况下,必要时关闭
      b
      ,然后使
      b
      引用与
      a
      相同的文件表条目。文件表条目是包含当前文件偏移量和文件状态标志的结构;多个文件描述符可以指向同一个文件表条目,这正是复制文件描述符时发生的情况。因此,
      dup2(a,b)
      具有使
      a
      b
      共享相同文件表条目的效果。因此,写入
      a
      b
      将最终写入同一文件。因此,关闭的文件是
      b
      ,而不是
      a
      。如果
      dup2(a,STDOUT\u FILENO)
      ,则关闭
      STDOUT
      ,并使
      STDOUT
      的文件描述符指向与
      a
      相同的文件表条目。任何写入
      stdout
      的程序都将改为写入文件,因为
      stdout
      的文件描述符指向您复制的文件
    更新

    因此,对于您的具体问题,在简要查看代码之后,我要说:

    您不应该在此处调用
    close(STDOUT\u FILENO)

    if (command->getOutputFD() == REDIRECT) {
        if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
            return false;
        if (dup2(outfd, STDOUT_FILENO) == -1)
            return false;
        close(STDOUT_FILENO);
    }
    
    如果关闭
    stdout
    ,将来尝试写入
    stdout
    时将出现错误。这就是为什么会出现
    ls:write error:Bad file descriptor
    。毕竟,
    ls
    正在写入
    stdout
    ,但您关闭了它。哎呀

    您正在反向操作:您想关闭
    outpd
    。您打开了
    outpd
    ,以便可以将
    STDOUT\u FILENO
    重定向到
    outpd
    ,一旦重定向完成,您就不再需要
    outpd
    ,您可以关闭它。但是您肯定不想关闭
    stdout
    ,因为这样做的目的是让
    stdout
    ᕕ( ᐛ )ᕗ ls -l
    total 68
    -rwxrwxrwx 1 cook cook   242 May 31 18:31 CMakeLists.txt
    -rwxrwxrwx 1 cook cook   617 Jun  1 22:40 Command.cpp
    -rwxrwxrwx 1 cook cook  9430 Jun  8 18:02 ExecuteExternalCommand.cpp
    -rwxrwxrwx 1 cook cook   682 May 31 18:35 ExecuteInternalCommand.cpp
    drwxrwxrwx 2 cook cook  4096 Jun  8 17:16 headers
    drwxrwxrwx 2 cook cook  4096 May 31 18:32 implementation files
    -rwxr-xr-x 1 cook cook 25772 Jun  8 18:12 LeShell
    -rwxrwxrwx 1 cook cook   243 Jun  5 13:02 Makefile
    -rwxrwxrwx 1 cook cook   831 Jun  3 12:10 Shell.cpp
    ᕕ( ᐛ )ᕗ ls -l > output.txt
    ls: write error: Bad file descriptor
    ᕕ( ᐛ )ᕗ ls -l | grep "cook"
    ᕕ( ᐛ )ᕗ 
    
    if (command->getOutputFD() == REDIRECT) {
        if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
            return false;
        if (dup2(outfd, STDOUT_FILENO) == -1)
            return false;
        close(STDOUT_FILENO);
    }
    
    if (command->getOutputFD() == REDIRECT) {
        if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
            return false;
        if (dup2(outfd, STDOUT_FILENO) == -1)
            return false;
        if (outfd != STDOUT_FILENO)
            close(outfd);
    }
    
    else if (command->getOutputFD() == REDIRECTAPPEND) {
        if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_APPEND)) == -1)
            return false;
        if (dup2(outfd, STDOUT_FILENO) == -1)
            return false;
        if (outfd != STDOUT_FILENO)
            close(STDOUT_FILENO);
    }