C fork / exec with non-blocking IO pipe

This seems to be a fairly common thing, and I managed to teach myself everything that I need for it to work, except that I now have one problem that challenges my troubleshooting.

int nonBlockingPOpen(char *const argv[]){ int inpipe; pid_t pid; /* open both ends of pipe nonblockingly */ pid = fork(); switch(pid){ case 0: /*child*/ sleep(1); /*child should open after parent has open for reading*/ /*redirect stdout to opened pipe*/ int outpipe = open("./fifo", O_WRONLY); /*SHOULD BLOCK UNTIL MAIN PROCESS OPENS FOR WRITING*/ dup2(outpipe, 1); fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK); printf("HELLO WORLD I AM A CHILD PROCESS\n"); /*This seems to be written to the pipe immediately, blocking or not.*/ execvp(*argv, argv); /*All output from this program, which outputs "one" sleeps for 1 second *outputs "two" sleeps for a second, etc, is captured only after the *exec'd program exits! */ break; default: /*parent*/ inpipe = open("./fifo", O_RDONLY | O_NONBLOCK); sleep(2); /*no need to do anything special here*/ break; } return inpipe; } 

Why doesn't the child process write its stdout to the pipe every time a line is created? Is there something I am missing in how execvp or dup2 work? I know that my approach to all of this is a bit strange, but I cannot find another way to programmatically display the output of closed source binary files.

+7
c pipe stdout nonblocking fork
source share
4 answers

I would suggest that you only get the exec'd program output after it exits, because after each message it does not flush . If so, you cannot do anything outside.

I'm not quite sure how this should relate to the choice between locking and non-blocking I / O in your question. A non-blocking recording may fail in whole or in part: instead of blocking the program until a room is available in the pipe, the call returns immediately and says that he could not write everything that he should have. Non-blocking I / O does not make the buffer larger and does not reset the output, and some of them may be poorly supported.

You cannot force a program designed for binary code only. If you think that non-blocking I / O was the solution to this problem, sorry, but I'm afraid it is pretty orthogonal.

EDIT: Well, if the exec'd program uses only the buffering provided by libc (does not implement its own) and is dynamically linked, you can force it to hide it by linking it to the modified libc, which flushes every entry. That would be a desperate measure. try only if all else fails.

+3
source share

When the process starts (via execvp () in your example), the behavior of standard output depends on whether the output device is a terminal or not. If this is not the case (and FIFO is not a terminal), then the output will be fully buffered rather than line buffered. There is nothing you can do about it; The (standard) C library does this.

If you really want it to work with buffering, you will need to provide the program with a pseudo-terminal as standard output. This falls into interesting areas - pseudo-terminals or ptys are not so easy to handle. For POSIX functions see:

  • grantpt() - grant access to a slave pseudo-terminal device
  • posix_openpt() - open a pseudo-terminal device
  • ptsname() - get the name of the slave pseudo-terminal device
  • unlockpt() - unlock a pair of master / slave pseudo-terminals
+3
source share

Why doesn't the child process write its stdout to the pipe every time a line is created?

How do you know that? You are not even trying to read the result with fifo.

NB by file name, I assume you are using fifo . Or is it a simple file?

And a little mistake in the child: after dup2() you need close(outpipe) .

fcntl (1, F_SETFL, fcntl (1, F_GETFL) | O_NONBLOCK);

Depending on which program you are running (), you may either lose some result or cause the program to crash, because writing to stdout may now fail with EWOULDBLOCK.

IIRC fifos has the same buffer size as pipes. The minimum POSIX level is 512 bytes, usually 4K or 8K.

You probably want to explain why you need it. Non-blocking IO has different semantics compared to blocking IO, and if your child process does not expect that you will encounter different problems.

printf ("HELLO WORLD I A CHILD PROCESS \ n");

stdout is buffered, followed by fflush(stdout) . (It is not possible to find documentation if exec () itself will run stdout or not.)

Is there something I am missing in how execvp or dup2 work? I know that my approach to all of this is a bit strange, but I cannot find another way to programmatically display the output of closed source binary files.

I would not play with non-blocking IO - and left it as it is in blocking mode.

And I would use pipe () instead of fifo. The Linux man pipe has a handy example with fork ().

Otherwise, this is a fairly common practice.

+1
source share

sleep() does not guarantee that the parent will open the pipe first, since Dummy00001 says that you should use the pipe() and not the named pipe. You should also check for execvp() and fork() errors, and you should not set the child side to non-blocking - this is the solution for the child process.

 int nonBlockingPOpen(char *const argv[]) { int childpipe[2]; pid_t pid; pipe(childpipe); pid = fork(); if (pid == 0) { /*child*/ /*redirect stdout to opened pipe*/ dup2(childpipe[1], 1); /* close leftover pipe file descriptors */ close(childpipe[0]); close(childpipe[1]); execvp(*argv, argv); /* Only reached if execvp fails */ perror("execvp"); exit(1); } /*parent*/ /* Close leftover pipe file descriptor */ close(childpipe[1]); /* Check for fork() failing */ if (pid < 0) { close(childpipe[0]); return -1; } /* Set file descriptor non-blocking */ fcntl(childpipe[0], F_SETFL, fcntl(childpipe[0], F_GETFL) | O_NONBLOCK); return childpipe[0]; } 
0
source share

All Articles