unixexecforkdup2

How do you use dup2 and fork together?


I'm taking an operating systems course and I'm having a hard time how input is redirected with dup2 when you have forks. I wrote this small program to try and get a sense for it but I wasn't successful in passing the output of a grand-child to a child. I am trying to mimick the unix command: ps -A | wc -l. I'm new to Unix, but I believe this should count the lines of the list of running processes I get. So my output should be a single number.

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

I don't have it in the code above, but here is my guess on how things should go down:

Question: Where do I redirect it to? I know it should be one of the file descriptors, but where should it be redirected so that wc can process it?

Question: How does wc receive the output? Through an execlp parameter? Or does the operating system check one of the file descriptors?

Which one of these is closed and left open for wc to receive and process ps's output? I keep thinking this needs to be thought of backwards since ps needs to give its output to wc...but that doesn't seem to make sense since both child and grand-child are being processed in parallel.

pipe dream


Solution

  • First off, let's fix your code so that we add a tiny bit more error-checking to it, and so that it works; replace the bottom bit with:

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

    Now, your questions:

    EDIT: Updated the arguments to execlp.