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?
write. Especially for pipes and sockets, partialwritedo happen - Basile Starynkevitch