7

我正在学习操作系统课程,当你有分叉时,我很难用 dup2 重定向输入。我编写了这个小程序来尝试理解它,但我没有成功地将孙子的输出传递给孩子。我正在尝试模仿 unix 命令: ps -A | 厕所-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 可以处理它?

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

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

  • 执行 wc -l。

其中哪一个是关闭并保持打开以便 wc 接收和处理 ps 的输出?我一直认为这需要倒过来考虑,因为 ps 需要将其输出提供给 wc ......但这似乎没有意义,因为孩子和孙子都是并行处理的。

白日梦

4

1 回答 1

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 可以处理它?

    文件描述符0、、12在 Unix 中是特殊的,因为它们是标准输入、标准输出和标准错误。wc从标准输入读取,所以无论是duped 到0.

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

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

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

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

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

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

编辑:将参数更新为execlp.

于 2012-01-14T09:01:27.790 回答