Question

I have a program that uses fork() to create child processes, and I want to have the children communicate back to the parent process using Unix pipes.

The problem is that multiple pipes don't seem to be created, or maybe there's a problem with my array. When I used prinf() in the parent program it reads identical data from every pipe, even though each child sends different data.

Here is my code:

// Variables
int pipes_count = 0;
int *pipes[MAXCLIENTS];
int new_pipefd[2];
int pipe_bytes;
char pipe_buffer[MAXDATASIZE];

while(1) {
    // Pipe creation 
    pipe(new_pipefd);
    pipes[pipes_count] = new_pipefd;
    pipes_count++;

    if (fork()) {
        // unrelated code for parent here
        close(new_pipefd[1]); // close the parent's write-end of the pipe
        break;
    } else {
        // unrelated code for child here
        close(new_pipefd[0]); // close the child's read-end of the pipe
        break;
    }

    if (some condition) { break; } // The parent will stop creating pipes
}

while(condition that guarantees this is the child) {
    write(new_pipefd[1], buffer, strlen(recv_buffer));
    close(new_pipefd[1]);
    return 0; // the child process ends
}

// This is a loop where the parent reads what the children sent
for (int i = 0; i < pipes_count; i++) {
    pipe_bytes = read(pipes[i][0], pipe_buffer, sizeof pipe_buffer);
    if (pipe_bytes == 0) {
        close(pipes[i][0]); 
    } else {
        printf("Testing: %s\n", pipe_buffer);
    }
}
Was it helpful?

Solution

As I noted in my comments, the problem is in the assignment pipes[pipes_count] = new_pipefd; at:

int pipes_count = 0;
int *pipes[MAXCLIENTS];
int new_pipefd[2];
int pipe_bytes;
char pipe_buffer[MAXDATASIZE];

while(1) {
    // Pipe creation 
    pipe(new_pipefd);
    pipes[pipes_count] = new_pipefd;
    pipes_count++;

The trouble is that the variable new_pipefd is an array, so you're copying the address of the same array into each of the elements of pipes, which means that the parent only has access to the last pipe that was created.

I think you should be using code more like:

int pipes_count = 0;
int pipes[MAXCLIENTS];  // Changed type!
int new_pipefd[2];
char pipe_buffer[MAXDATASIZE];

while (1)
{
    // Pipe creation 
    pipe(new_pipefd);
    pipes[pipes_count++] = new_pipefd[0];  // Just the read end of the pipe

    if (fork())
    {
        // unrelated code for parent here
        close(new_pipefd[1]); // close the parent's write-end of the pipe
        // break;             // This break is not wanted
    }
    else
    {
        // unrelated code for child here
        close(new_pipefd[0]); // close the child's read-end of the pipe
        break;
    }

    if (some condition)
        break;        // The parent will stop creating pipes
}

while (condition that guarantees this is the child)
{
    write(new_pipefd[1], buffer, strlen(recv_buffer));
    close(new_pipefd[1]);
    return 0; // the child process ends
}

// This is a loop where the parent reads what the children sent
for (int i = 0; i < pipes_count; i++) {
    int pipe_bytes = read(pipes[i], pipe_buffer, sizeof(pipe_buffer));
    if (pipe_bytes != 0)
        printf("Testing: %.*s\n", pipe_bytes, pipe_buffer); // Safe!
    close(pipes[i]); 
}

Were it my code, I'd have a function (which I traditionally call be_childish()) to invoke in the 'if it is a child' block of code in the loop. The function would never return, and would be passed whatever resources it needs (new_pipefd for sure, maybe other information too). I often have a function be_parental() to do the parental activities. I find this cleans up most of the code, forcing clean separation of the activities.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top