你如何一起使用dup2和fork?

Shr*_*ers 6 unix fork exec dup2

我正在学习操作系统课程,当你有叉子时,我很难用dup2重定向输入.我写了这个小程序,试图对它有所了解但是我没有成功地把一个大孩子的输出传给一个孩子.我试图模仿unix命令:ps -A | wc -l.我是Unix的新手,但我相信这应该算上我得到的正在运行的进程列表的行.所以我的输出应该是一个数字.

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>

using namespace std;

int main( int argc, char *argv[] )  {

    char *searchArg = argv[ 1 ];
    pid_t pid;

    if ( ( pid = fork() ) > 0 ) {
        wait( NULL );
        cout << "Inside parent" << endl;
    } 
    else if ( pid == 0 ) {
            int fd1[ 2 ];
            pipe( fd1 );
            cout << "Inside child" << endl;

            if ( pid = fork() > 0 ) {
                dup2( fd1[ 0 ], 0 );
                close( fd1[ 0 ] );
                execlp( "/bin/wc", "-l", NULL );
            }
            else if ( pid == 0 ) {
                cout << "Inside grand child" << endl;
                execlp( "/bin/ps", "-A", NULL );
            }
        }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我在上面的代码中没有它,但这是我对如何应该发生故障的猜测:

  • 我们需要重定向命令ps -A的标准输出(通常打印到屏幕上的任何内容,对吗?),以便wc -l命令可以使用它来计算行数.
  • 此标准输出可以使用dup2重定向,如dup2(?,1),这意味着将标准输出重定向到?.然后你关闭?

问题:我在哪里重定向到?我知道它应该是文件描述符之一,但应该在哪里重定向,以便wc可以处理它?

  • wc以某种方式接收标准输出.

问题:wc如何收到输出?通过execlp参数?或者操作系统是否检查了其中一个文件描述符?

  • 执行wc -l.

其中哪一个是关闭的并且保持开放状态以便wc接收和处理ps的输出?我一直认为这需要被认为是倒退,因为ps需要将其输出提供给wc ......但这似乎没有意义,因为并行处理子和孙子.

白日梦

Dan*_*tin 5

首先,让我们修复您的代码,以便为它添加一点点错误检查,以便它能够工作;将底部位替换为:

else if ( pid == 0 ) {
        int fd1[ 2 ];
        pipe( fd1 );
        cout << "Inside child" << endl;

        if ( (pid = fork()) > 0 ) {
            if (dup2( fd1[ 0 ] , 0 ) < 0) {
              cerr << "Err dup2 in child" << endl;
            }
            close( fd1[ 0 ] );
            close( fd1[ 1 ] ); // important; see below
            // Note: /usr/bin, not /bin
            execlp( "/usr/bin/wc", "wc", "-l", NULL );
            cerr << "Err execing in child" << endl;
        }
        else if ( pid == 0 ) {
            cout << "Inside grand child" << endl;
            if (dup2( fd1[ 1 ] , 1 ) < 0) {
              cerr << "Err dup2 in gchild" << endl;
            }
            close( fd1[ 0 ] );
            close( fd1[ 1 ] );
            execlp( "/bin/ps", "ps", "-A", NULL );
            cerr << "Err execing in grandchild" << endl;
        }
}
Run Code Online (Sandbox Code Playgroud)

现在,你的问题:

  • 问题:我应该将其重定向到哪里?我知道它应该是文件描述符之一,但是应该将其重定向到哪里以便 wc 可以处理它?

    文件描述符01、 和2在 Unix 中很特殊,因为它们是标准输入、标准输出和标准错误。wc从标准输入读取,因此无论duped 为0.

  • 问题:wc 如何接收输出?通过 execlp 参数?或者操作系统是否检查文件描述符之一?

    一般来说,在进程将其映像替换为 后exec,它将拥有之前拥有的所有打开的文件描述符exec。(除了那些CLOSE_ON_EXEC设置了标志的描述符,但现在忽略它)因此,如果您要执行dup2某些操作0,那么wc将读取它。

  • 其中哪一个被关闭并保持打开状态以供 wc 接收和处理 ps 的输出?

    如上所示,您可以在子代和孙代中关闭管道的两端,这样就可以了。事实上,标准做法会建议您这样做。然而,在这个特定示例中,唯一真正必要的close行是我评论为“重要”的行 - 即关闭子项中管道的写入端

    这个想法是这样的:孩子和孙子在启动时管道的两端都打开。现在,dup我们已经连接wc到管道的读取端。wc将继续吸吮该管道,直到管道写入端的所有描述符都关闭,此时它将看到它到达文件末尾并停止。现在,在孙子中,我们可以不关闭任何东西,因为ps -A不会对任何描述符做任何事情,而是写入描述符1,并且在ps -A完成吐出有关某些进程的内容后,它将退出,关闭所有东西它有过。在子进程中,我们实际上不需要关闭存储在中的读取描述符fd[0],因为wc不会尝试从描述符以外的任何内容读取0。然而,我们确实需要关闭子进程中管道的写入端,因为否则wc管道将永远不会完全关闭。

    正如您所看到的,除了标记为“重要”的那一行之外,我们实际上不需要任何行的原因close取决于行为方式wcps行为的细节,因此标准做法是关闭管道的末端您没有完全使用,并保持仅使用一个描述符的一端打开。dup2由于您在两个进程中都使用,因此这意味着close上面的四个语句。

编辑:将参数更新为execlp.