你如何一起使用dup2和fork?

时间:2012-01-14 07:46:57

标签: unix exec fork 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;
}

我在上面的代码中没有它,但这是我对事情应该如何下降的猜测:

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

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

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

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

  • 执行wc -l。

关闭哪一项并让wc接收和处理ps的输出?我一直认为这需要被认为是倒退,因为ps需要将其输出提供给wc ......但这似乎没有意义,因为并行处理子孙和孙子。

pipe dream

1 个答案:

答案 0 :(得分:4)

首先,让我们修复你的代码,以便我们为它添加更多的错误检查,以便它可以工作;用下面的位替换:

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;
        }
}

现在,您的问题:

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

    文件描述符012在Unix中很特殊,因为它们是标准输入,标准输出和标准错误。 wc从标准输入读取,因此dup 0的任何内容均为exec

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

    通常,在进程将其图像与exec交换后,它将具有CLOSE_ON_EXEC之前的所有打开文件描述符。 (除了那些设置了dup2标志的描述符,但暂时忽略它们)因此,如果你0 wcclose将读取它。

  • 关闭哪一项并让wc接收和处理ps的输出?

    如上图所示,你可以关闭孩子和孙子的管道的两端,这样就可以了。事实上,标准练习会建议您这样做。但是,在这个具体示例中唯一真正必要的dup行是我评论为&#34; important&#34; - 关闭了孩子中管道的端。

    这个想法是这样的:孩子和大孩子在他们开始时都会打开管道的两端。现在,通过wc我们已将wc连接到管道的读取端。 ps -A将继续吸吮该管道,直到管道写入端的所有描述符都关闭,此时它会看到它到达文件的末尾并停止。现在,在祖母中,我们可以逃避不关闭任何东西,因为1不会对任何描述符做任何事情,而是写入描述符ps -A,并且fd[0]之后1}}完成吐出关于某些进程的东西,它将退出,关闭它拥有的所有东西。在孩子中,我们并不需要关闭wc中存储的读取描述符,因为0不会尝试读取除了描述符wc之外的任何内容。但是,我们确实需要关闭子管道的写入端,否则close就会坐在那里,管道永远不会完全关闭。

    正如您所看到的,为什么我们并不真正需要任何wc行,除了标有&#34;重要&#34;取决于psdup2将如何表现的详细信息,因此标准做法是关闭您未完全使用的管道的末端,并保持开放结束仅使用一个描述符。由于您在两个流程中都使用close,这意味着上述四个execlp语句。

编辑:更新了{{1}}的参数。