0
votes

I encountered a strange bug when working with unix pipes. I wrote a short program to showcase the problem.

Here is the code:

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


int main(int argc, char*args[]){
    int fd[2];
    pipe(fd);
    int pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{
        close(fd[1]);
    }
    pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{
        close(fd[1]);
    }
    //close(fd[1]);
    sleep(5);

    const char* msg = "I'm here\n";
    size_t len = strlen(msg) + 1; // +1 for null char
    char str[len];
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    /*does other work*/
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    printf("finished read\n");
    /*does other work*/
    wait(NULL);
    return 0;
}

The parent process forks two children and creates a pipe. Two child processes write to the pipe, parent read from the pipe.

Parent process should recover all information wrote by child.

However, when I run the above program, parent prints out

Nothing from child
finished read

Why?

Interestingly, if I write a single close(fd[1]) like this:

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


int main(int argc, char*args[]){
    int fd[2];
    pipe(fd);
    int pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{

    }
    pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{

    }
    close(fd[1]);//HERE IS THE DIFFERENCE
    sleep(5);

    const char* msg = "I'm here\n";
    size_t len = strlen(msg) + 1; // +1 for null char
    char str[len];
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    /*does other work*/
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    printf("finished read\n");
    /*does other work*/
    wait(NULL);
    return 0;
}

It works, it also works if I don't have close(fd[1]) at all. I'm at a lost, why would the position of close() affect the reading of pipes?

1
Why do you think it's OK to close fd[1] twice? - n. 1.8e9-where's-my-share m.
In the first example, you close the write side of the pipe before you fork the second child, so that child cannot write into the pipe. You fix it by moving the close. What's the question? - William Pursell
Ah, I get it. Thank you, I forgot that the second child would have the writing end closed! - Noodles
You should always test the return count of write. Especially for pipes and sockets, partial write do happen - Basile Starynkevitch

1 Answers

1
votes

I'm not sure if this is your problem, but this statement is clearly wrong:

write(fd[1], "I'm here\n", 256);

You are writing 256 bytes from the source buffer, which is a very short static string. I think you want to say this:

const char* msg = "I'm here\n";
size_t len = strlen(msg); // could be strlen(msg)+1 - see discussion in comments.
write(fd[1], "I'm here\n", len);