Home > Software engineering >  Two way communicating using pipe
Two way communicating using pipe

Time:05-11

I have a small program to practice pipe and processes in C. There is a parent process and there're two child processes (child_a, child_b). There're two array of pointers (each pointer points to a struct (Rabbit)) and the parent sends one of them to child_a and the other to child_b (using pipes). I'm using 4 pipes for communicating: pipefd_a1 and pipefd_b1 for parent->child and the other 2 for the child->parent way. The child processes modify the elements then send them back to the parent. Then the parent writes something to the console. So it's working until the last child process read the last data from pipe and then the program hangs. It writes a number to the console (2034, 10987, etc.) and then hangs. How can I solve this problem and why does not return from the while loop? Didn't I close all the pipes before reading from it?

typedef struct Rabbit {
    char* name;
    char* district;
    unsigned part_count;
    unsigned eggs_count;
} Rabbit;    

int pipefd_a1[2], pipefd_a2[2];
int pipefd_b1[2], pipefd_b2[2];
pid_t child_a, child_b;

// Pipe error checking
...

Rabbit** rabbits_a = (Rabbit**)malloc(sizeof(Rabbit*) * size);
Rabbit** rabbits_b = (Rabbit**)malloc(sizeof(Rabbit*) * size);

// Setting the array values (with malloc calls ofc)
...
// count_a is the size of rabbits_a, count_b is the size of rabbits_b

for (unsigned i = 0; i < count_a;   i) {
    Rabbit rabbit = {rabbits_a[i]->name, rabbits_a[i]->district, rabbits_a[i]->part_count, rabbits_a[i]->eggs_count};
    write(pipefd_a1[1], &rabbit, sizeof(Rabbit));
}
for (unsigned i = 0; i < count_b;   i) {
    Rabbit rabbit = {rabbits_b[i]->name, rabbits_b[i]->district, rabbits_b[i]->part_count, rabbits_b[i]->eggs_count};
    write(pipefd_b1[1], &rabbit, sizeof(Rabbit));
}

child_a = fork();

if (child_a == 0) {    // Child A
    close(pipefd_a2[0]);
    close(pipefd_a1[1]);
    Rabbit* rabbits = (Rabbit*)malloc(sizeof(Rabbit) * size);
    Rabbit rabbit;
    unsigned count_rabbits = 0;
    while (read(pipefd_a1[0], &rabbit, sizeof(Rabbit))) {
        // Do something with rabbit
        rabbits[count_rabbits  ] = rabbit;
    }
    close(pipefd_a1[0]);

    for (unsigned i = 0; i < count_rabbits;   i) {
        write(pipefd_a2[1], &rabbits[i], sizeof(Rabbit));
    }
    close(pipefd_a2[1]);
} else {
    child_b = fork();

    if (child_b == 0) {    // Child B
        close(pipefd_b2[0]);
        close(pipefd_b1[1]);
        Rabbit* rabbits = (Rabbit*)malloc(sizeof(Rabbit) * size);
        Rabbit rabbit;
        unsigned count_rabbits = 0;
        while (read(pipefd_b1[0], &rabbit, sizeof(Rabbit))) {
            // Do something with rabbit
            rabbits[count_rabbits  ] = rabbit;
        }
        // The execution does not reach this point
        close(pipefd_b1[0]);

        for (unsigned i = 0; i < count_rabbits;   i) {
            write(pipefd_b2[1], &rabbits[i], sizeof(Rabbit));
        }
        close(pipefd_b2[1]);
    } else {    // Parent
        pid_t pids[] = {child_a, child_b};
        pid_t returned_pid;

        for (unsigned i = 0; i < 2;   i) {
            struct stat dummy;
            char buf[70];
            sprintf(buf, "ps -e | awk '{ print $1 }' | grep %d", pids[i]);
            int result = system(buf);
            if (result != 0) {
                continue;
            }
            do {
                returned_pid = wait(NULL);
            } while (returned_pid != pids[i]);
        }
    }
}

CodePudding user response:

Here's my edit of your code. I've reorganized the sequence of operations so that the child processes cannot 'cheat' by having the information that the parent is supposed to send to the children already available. This code compiles; I've not run it because I'm not good at breeding rabbits and your code is incomplete at the point where the rabbit information should be created.

Note the plethora of close() calls. Each child needs to close all four pipe descriptors for the other child, as well as the two it won't use. The parent process must close eight pipe descriptors. I've rewritten the wait processing — I don't think what you had is appropriate. All else apart, if the 'wrong' child died first, you'd have problems. And running ps | awk | grep is not good — use awk alone or grep alone but not both.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct Rabbit
{
    char *name;
    char *district;
    unsigned part_count;
    unsigned eggs_count;
} Rabbit;

int main(void)
{
    int pipefd_a1[2], pipefd_a2[2];
    int pipefd_b1[2], pipefd_b2[2];
    pid_t child_a, child_b;

    if (pipe(pipefd_a1) != 0 ||
        pipe(pipefd_a2) != 0 ||
        pipe(pipefd_b1) != 0 ||
        pipe(pipefd_b2) != 0)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    size_t size = 20;
    Rabbit **rabbits_a = (Rabbit **)malloc(sizeof(Rabbit *) * size);
    Rabbit **rabbits_b = (Rabbit **)malloc(sizeof(Rabbit *) * size);

    unsigned count_a = 10;
    unsigned count_b = 8;

    if ((child_a = fork()) < 0)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (child_a == 0)  // Child A
    {
        /* Not using b1 or b2 pipes at all */
        close(pipefd_b1[0]);
        close(pipefd_b1[1]);
        close(pipefd_b2[0]);
        close(pipefd_b2[1]);

        close(pipefd_a1[1]);
        close(pipefd_a2[0]);

        Rabbit *rabbits = (Rabbit *)malloc(sizeof(Rabbit) * size);
        Rabbit rabbit;
        unsigned count_rabbits = 0;
        while (read(pipefd_a1[0], &rabbit, sizeof(Rabbit)))
        {
            // Do something with rabbit
            rabbits[count_rabbits  ] = rabbit;
        }
        close(pipefd_a1[0]);

        for (unsigned i = 0; i < count_rabbits;   i)
        {
            write(pipefd_a2[1], &rabbits[i], sizeof(Rabbit));
        }
        close(pipefd_a2[1]);
    }
    else if ((child_b = fork()) == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (child_b == 0)
    {
        /* Not using a1 or a2 pipes at all */
        close(pipefd_a1[0]);
        close(pipefd_a1[1]);
        close(pipefd_a2[0]);
        close(pipefd_a2[1]);

        close(pipefd_b1[1]);
        close(pipefd_b2[0]);
        Rabbit *rabbits = (Rabbit *)malloc(sizeof(Rabbit) * size);
        Rabbit rabbit;
        unsigned count_rabbits = 0;
        while (read(pipefd_b1[0], &rabbit, sizeof(Rabbit)))
        {
            rabbits[count_rabbits  ] = rabbit;
        }
        close(pipefd_b1[0]);

        for (unsigned i = 0; i < count_rabbits;   i)
        {
            write(pipefd_b2[1], &rabbits[i], sizeof(Rabbit));
        }
        close(pipefd_b2[1]);
    }
    else
    {
        for (unsigned i = 0; i < count_a;   i)
        {
            Rabbit rabbit = {rabbits_a[i]->name, rabbits_a[i]->district, rabbits_a[i]->part_count, rabbits_a[i]->eggs_count};
            write(pipefd_a1[1], &rabbit, sizeof(Rabbit));
        }

        for (unsigned i = 0; i < count_b;   i)
        {
            Rabbit rabbit = {rabbits_b[i]->name, rabbits_b[i]->district, rabbits_b[i]->part_count, rabbits_b[i]->eggs_count};
            write(pipefd_b1[1], &rabbit, sizeof(Rabbit));
        }

        /* Close pipes so child processes get EOF */
        close(pipefd_a1[0]);
        close(pipefd_a1[1]);
        close(pipefd_a2[0]);
        close(pipefd_a2[1]);
        close(pipefd_b1[0]);
        close(pipefd_b1[1]);
        close(pipefd_b2[0]);
        close(pipefd_b2[1]);

        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("Child %d exited with status 0x%.4X\n", corpse, status);
    }
    return 0;
}
  • Related