Why is output of parent process blocked by child p

2019-05-24 23:05发布

问题:

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;
  }
}

回答1:

The parent process is reading the child's message via fgets(). It will continue to read until one of three things happens:

  • enough bytes have been read to fill the buffer, less one for a string terminator
  • a newline is read
  • end-of-file is encountered

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.



回答2:

If you close the file in the client right after flushing, it will work as expected:

fflush(fpc);
fclose(fpc);

Output:

[dbush] /tmp/x1 hello
hello
[dbush] This is the child process. Closing


回答3:

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 raw IO syscalls.

If you substitute, e.g.:

char buf[100];
read(pipefd[0], buf, 100);
fprintf(stdout,"%s\n",buf);

for the fgets part, you'll get your message right away, because read will give you whatever's available and return (if there's at least 1 byte in the pipe buffer at the time you make the read request), whereas fgets 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 unsatisfiable read syscall that fgets has issued).



标签: c++ fork