In my code below, I forked my process into a parent and child process. In the child process, I sent the c string argv[1] to the parent process to be printed. Then I made the child process sleep for 4 seconds before printing "This is the child process. Closing\n".
In the parent process, I want the string from the child process to be printed to stdout as soon as I receive it from the child process.
The problem arises here. Instead of immediately printing argv[1] in the parent process before the string "This is the child process. Closing\n" is printed 4 seconds later, what happens is this:
$ g++ -std=c++11 printchild.cpp -o printchild
$ ./printchild helloworld
1) 4 seconds passes
2) "This is the child process. Closing\n" is printed
3) "helloworld" is printed
Why is the output from the parent process blocked by the child process?
// printchild.cpp
#include <chrono>
#include <thread>
#include <cstdio>
#include <unistd.h>
#include <cstdlib>
int main(int argc, char * argv[]){
int pipefd[2];
pid_t cpid;
if(argc != 2){
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if(pipe(pipefd) == -1){
perror("fork");
exit(EXIT_FAILURE);
}
cpid = fork();
if(cpid == 0){
close(pipefd[0]);
FILE *fpc = fdopen(pipefd[1],"wb");
fprintf(fpc,"%s",argv[1]);
fflush(fpc);
std::this_thread::sleep_for(std::chrono::seconds(4));
fprintf(stdout,"This is the child process. Closing\n");
return 0;
}else if(cpid == -1){
perror("Error in forking");
exit(EXIT_FAILURE);
}else{
char str[80];
close(pipefd[1]);
FILE* fp = fdopen(pipefd[0], "rb");
fgets(str,80,fp);
fprintf(stdout,"%s\n",str);
return 0;
}
}
The parent process is reading the child's message via
fgets()
. It will continue to read until one of three things happens:The child does not send enough bytes to exhaust the buffer, and it does not send a newline, so the parent's
fgets()
does not return until the child's end of the pipe is closed upon its exit.You can fix this in the child by having it either terminate the message with a newline or close the stream immediately after writing.
Your problem is the
fgets
call. It tries to fulfill your request fully, and that's the cause of the blocking.If you're prototyping some IPC, I recommend you ditch
stdio
and go for the rawIO
syscalls.If you substitute, e.g.:
for the
fgets
part, you'll get your message right away, becauseread
will give you whatever's available and return (if there's at least 1 byte in the pipe buffer at the time you make theread
request), whereasfgets
won't back off until it has read all the requested bytes or until it figures out it can't (EOF or an IO error).(To split hairs, it's not
fgets
that's blocking (userspace code alone can't block) -- it's another momentarily unsatisfiableread
syscall thatfgets
has issued).If you close the file in the client right after flushing, it will work as expected:
Output: