0
votes

How can I make functional pipes between 3 or more children?

I use this command:

./function n with n the number of children.

And the the father makes n pipes and n children.

After that the children close the pipes they will not use.

Then the father writes something in the first pipe, and the first children read it, and write it to the next pipe. The last child should write the content in the stdout.

When I use only 1 child it works perfect. When I try to use 2 children, the last one reads from the pipe: -1 characters and when I try to use 3 children, I got segment violation and the code doesn't work.

Hope you can help me, here is the code.

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

void use_pipes(int i);
void write_father(void);

int (*pp)[2];
int childrens;

int main(int argc, char* argv[]) {

    childrens = atoi(argv[1]);
    int p, i, j, aux;

    printf("I'm the parent and i'm making %d childrens\n", childrens);
    pp = malloc(sizeof (*pp) * childrens);
    for (i = 0; i < childrens; i++) {

        if (pipe(pp[i]) < 0) {
            perror("Error in pipe()");
            exit(1);
        }

    }//for

    for (i = 0; i < childrens; i++) {
        printf("I'm the parent pid: %d and I'm going to make the child nº: %d\n", getpid(), i);
        p = fork();

        switch (p) {
            case -1:
                printf("We couldn't make the child nº:%d\n", i);
                exit(1);
            case 0:
                //printf("I'm nº: %d, and I start my code\n",i);

                for (aux = 0; aux < childrens; aux++) {
                    if (aux == i) {
                        //printf("I'm the children nº: %d and I'm going to close the pipe[%d] of writing\n", i,i);
                        close(pp[i][1]);
                    } else if (aux == i + 1) {
                        //printf("I'm the children nº: %d and I'm going to close the pipe[%d] of reading\n", i,aux);
                        close(pp[aux][0]);
                    } else {
                        //printf("I'm the children nº: %d and I'm going to close the pipe[%d]\n", i,aux);
                        close(pp[aux][0]);
                        close(pp[aux][1]);
                    }
                }//for

                //printf("I finished to close my unused pipes\n");
                use_pipes(i);
                exit(0);

            default:
                break;
        }//switch
    }//for of fork
    close(pp[0][0]);
    for (j = 1; j < childrens; j++) {
        close(pp[j][0]);
        close(pp[j][1]);

    }//for
    write_father();
    int status;
    for (i = 0; i < childrens; i++) {
        wait(&status);
    }

}//main

void write_father() {
    int wroten;
    char buffer[50] = "Hello World";
    int len = strlen(buffer);
    wroten = write(pp[0][1], buffer, len);
    printf("The parent wrote: %d characters\n", wroten);
}

void use_pipes(int n_children) {
    int readed;
    char buffer [50];
    readed = read(pp[n_children][0], buffer, sizeof (buffer));
    printf("Children %d readed from pipe[%d][0]: %d characters\n", n_children, n_children, readed);
    if (n_children != childrens - 1) {
        printf("Children %d write in the pipe[%d][1]: %d characters\n", n_children, n_children + 1, readed);
        write(pp[n_children + 1][1], buffer, readed);
    } else
        write(1, buffer, readed);
}

EDIT: I posted the code that worked for me

1
Please fix your indentation. Experienced programmers generally always prefer to use proper indentation because it helps to visually identify the control flow and variable scope. You are asking for help, so it makes sense to do what you can to make it easier for others to help you. - davmac
I tried to do my best, but when I post the code here I have to manually put 4 spaces so the web recognize it like code, and the indentation becomes crazy. I would like to know if there is a way to mark part of the text like code and that the indentation stay like when I copy it from my file. - DavidCG
Select your code and press the "{}" button in the editing toolbar (or as per the tooltip, press ctrl+K). - davmac
Hey thank you so much. I did already a few questions here and didn't know how to fix that, so from now on I will be doing it right :) - DavidCG

1 Answers

1
votes

You have several problems.

  • int* pp[2] doesn't mean what you think it means. This is an array of two pointers to int, but what you appear to want is a pointer to an array of two int. That would be int (*pp)[2]. Your segmentation fault is likely related to this.

  • having corrected the type of pp, you next must allocate it correctly; specifically, all in one chunk. This is the cleanest way to accomplish that (note that there is no loop involved, and that the needed size is defined in terms of the size of the pointer's target):

    pp = malloc(childrens * sizeof(*pp));

If you want the parent to create all the pipes in advance then you do need to use a loop for that.

  • The parent should close unused pipe ends once each, but you have it closing them once per child forked. Move that out of the loop.

  • Although C allows it, it is poor form to allow control to reach the closing bracket of main(). Use a return statement or call exit() instead.

  • You assume that your I/O will not split the data, so that single write() and read() calls are sufficient. That will probably work for you, but it is not, in fact, reliable. You should loop your write()s and read()s to transfer the full number of bytes via multiple calls when necessary. Use the return values of these functions to help with this.

  • The preceding implies that you need to communicate between processes how many bytes to read in any one message. You could do that with a terminator (e.g. a newline or null byte), or by first sending a (fixed-size) integer that says how many characters follow in the same message.