bhristov
bhristov

Reputation: 3197

Problem with C++ pipes when doing a read and write on the same pipe in a process

I am learning about pipes and forks. I am trying to get the count of kworker processes by using pipes and forks to represent ps -A | grep kworker | wc -l. My code is working correctly until the point where I am doing the pipes for the grep statement.

I run my code like this: ./a.out kworker

#include <iostream>
#include <unistd.h> // for fork() 
#include <sys/wait.h> // for wait() 

using std::cout;
using std::endl;

int main(int num = 1, char * args[] = NULL)
{
    int pipefd[2];
    pipe(pipefd);
    char * p[3];

    /*
       wait for grandchild to complete ps - A
       wait for child to complete grep kworker
       execute wc -l
    */

    p[2] = NULL;
    pid_t id1 = fork(); // get child process

    if(id1 == -1) 
    {
        perror("fork");

        close(pipefd[0]);
        close(pipefd[1]);
    }
    else if(id1 == 0) // child process
    {
        pid_t id2 = fork(); // get grandchild process

        if(id2 == 0) // grandchild process
        {
            p[0] = "ps";
            p[1] = "-A";

            dup2(pipefd[1], STDOUT_FILENO);

            close(pipefd[0]); // close read
            close(pipefd[1]); 

            execvp(p[0], p);

            perror("exec");
            exit(1);
        }
        else if(id2 > 0) // child process
        {
            // The problem is in this if-statement

            close(pipefd[1]); 

            wait(0);

            p[0] = "grep";
            p[1] = args[1];

            // I get output from the STDOUT

            dup2(pipefd[0], STDIN_FILENO); // read from pipe
            dup2(pipefd[1], STDOUT_FILENO); // write to pipe

            close(pipefd[1]);

            execvp(p[0], p);
        }
    }
    else if(id1 > 0) // parent process
    {
        // close pipes first
        close(pipefd[1]); // close unused write end

        wait(0);

        p[0] = "wc";
        p[1] = "-l";

        dup2(pipefd[0], STDIN_FILENO); // read from pipe

        close(pipefd[1]);
        close(pipefd[0]);

        if(execvp(p[0], p) == -1)
        {
            perror("exec");
            exit(1);
        }
    }
}

The output that the code gives me:

      6 ?        00:00:00 kworker/0:0H-events_highpri
     20 ?        00:00:00 kworker/1:0H-kblockd
     26 ?        00:00:00 kworker/2:0H-kblockd
     32 ?        00:00:00 kworker/3:0H-kblockd
     50 ?        00:00:00 kworker/1:1-events
    172 ?        00:00:00 kworker/u17:0-rb_allocator
    289 ?        00:00:00 kworker/3:1H
    290 ?        00:00:00 kworker/1:1H-events_highpri
    296 ?        00:00:00 kworker/0:1H-kblockd
    360 ?        00:00:00 kworker/2:1H-events_highpri
    550 ?        00:00:00 kworker/u17:1-rb_allocator
   1340 ?        00:00:01 kworker/2:4-events
   1374 ?        00:00:00 kworker/0:0-events
   2751 ?        00:00:00 kworker/3:2-events
   2782 ?        00:00:00 kworker/1:2-mm_percpu_wq
   5389 ?        00:00:00 kworker/0:1-events
   6539 ?        00:00:00 kworker/u16:3-events_unbound
   6599 ?        00:00:00 kworker/3:1
   6618 ?        00:00:00 kworker/2:1-events
   6705 ?        00:00:00 kworker/u16:0-events_unbound
   6815 ?        00:00:00 kworker/u16:1-events_unbound
   7283 ?        00:00:00 kworker/u16:2-events_unbound
   0

I am expecting the output to be the same as:

ps -A | grep kworker | wc -l
21

What could be causing this issue? I looked through other questions here and could not find a solution to this.

Adding a second pipe resolved the issue. We cannot both read and write to the same pipe.

Upvotes: 0

Views: 696

Answers (1)

Look:

close(pipefd[1]); 
dup2(pipefd[1], STDOUT_FILENO);

You close one of the FDs. Then you use dup2 with it. Because the FD is closed, dup2 returns an error code and doesn't do anything. So grep's stdout is still the normal stdout.

Your code has other problems. This is the one you asked about.


Elaboration about "other problems": Your code tries to use the same pipe twice. ps and grep read from the pipe, and grep and wc read from the pipe. This means data can go from ps directly to wc, or from grep back to itself, which isn't what you want. You should use two separate pipes - one to connect ps to grep and one to connect grep to wc.

Upvotes: 1

Related Questions