unix - dup2 和 fork 怎么一起用​​?

标签 unix exec fork dup2

我正在学习操作系统类(class),当你有 fork 时,我很难用 dup2 重定向输入。我编写了这个小程序来尝试了解它,但是我没有成功地将孙子的输出传递给 child 。我试图模仿 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 可以处理它?
  • wc 以某种方式接收标准输出。

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

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

    pipe dream

    最佳答案

    首先,让我们修复您的代码,以便我们向它添加更多的错误检查,使其工作;用以下内容替换底部位:

    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 , 1 , 和 2 Unix 中的特殊之处在于它们是标准输入、标准输出和标准错误。 wc从标准输入读取,所以不管是 dup编辑至 0 .
  • 问题:wc 如何接收输出?通过execlp参数?或者操作系统是否检查文件描述符之一?
    通常,在进程用 exec 换出其镜像后,它将拥有之前所有的打开文件描述符 exec . (除了那些设置了 CLOSE_ON_EXEC 标志的描述符,但暂时忽略它)因此,如果你 dup2有事要0 ,然后 wc会读。
  • 其中哪一个是关闭的并保持打开状态,以便 wc 接收和处理 ps 的输出?
    如上所示,您可以关闭子代和孙代管道的两端,这样就可以了。事实上,标准做法会建议您这样做。然而,唯一真正需要的close这个特定示例中的那一行是我评论为“重要”的那一行 - 即关闭子进程中管道的写端。
    这个想法是这样的: child 和孙子在开始时都打开管道的两端。现在,通过 dup我们已连接 wc到管道的读取端。 wc将继续吸入该管道,直到管道写入端的所有描述符都关闭,此时它将看到它到达文件末尾并停止。现在,在孙子中,我们可以不关闭任何东西,因为 ps -A不会对任何描述符做任何事情,而是写入描述符 1 ,以及之后 ps -A完成吐出有关它将退出的某些进程的内容,关闭它拥有的所有内容。在子进程中,我们实际上并不需要关闭存储在 fd[0] 中的读取描述符。因为 wc除了描述符 0 之外,不会尝试读取任何内容.但是,我们确实需要关闭子进程中管道的写端,否则 wc只是坐在那里,管子永远不会完全关闭。
    正如你所看到的,为什么我们真的不需要任何 close 的原因除了标记为“重要”的那一行,取决于 wc 的详细信息和 ps会起作用,所以标准做法是关闭您没有完全使用的管道的末端,并保持打开您只使用一个描述符的末端。由于您使用的是 dup2在这两个进程中,这意味着四个 close声明如上。

  • 编辑:将参数更新为 execlp .

    关于unix - dup2 和 fork 怎么一起用​​?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8860895/

    相关文章:

    bash - vim:如果为空,则将第二列空白替换为同一列中的值

    javascript - 从 PHP 执行 Puppeteer 突然停止工作

    bash - 如果其中一个文件失败,如何退出 find -exec

    node.js - 如何确保nodejs集群将进程分配给不同的核心?

    mysql - 如何将 phpmyadmin 安装到虚拟服务器?

    java - 常见unix命令(特别是dd)的java库?

    linux - unix join命令返回一个文件中的所有列

    php - 通过 php exec() 运行 mysqldump,管道到 gzip,返回 mysqldump stderr

    GitHub - 组织内的自 fork 存储库

    c - fork 和线程有什么区别?