I am trying to interact with an external program in C using pipe
, fork
and exec
. I want to force the external program to perform unbuffered I/O. Here's a relevant snippet from my code so far:
...
pid = fork();
if (pid == (pid_t) 0)
{
/* child process */
/* make pipe connections to standard streams */
dup2(wpipe[0], STDIN_FILENO);
dup2(rpipe[1], STDOUT_FILENO);
/* close pipe endings */
close(wpipe[0]); close(rpipe[0]);
close(wpipe[1]); close(rpipe[1]);
/* unbuffered I/O */
setvbuf(stdin, NULL, _IONBF, BUFSIZ);
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
if (execl("path/to/some_binary", "path/to/some_binary", (char *) NULL) == -1)
{
fprintf(stderr, "exec failed\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
...
This does not work because streams do not survive across exec
calls. So using setvbuf
to force unbuffered I/O does not work, because the program image (some_binary
) creates stdin and stdout streams of its own, and does not use the streams that I called setvbuf
on.
The program does work when I re-build some_binary
after adding the setvbuf
calls in its code. But how can this be done if you do not have any control over the binary you are passing to exec
? How can this be made to work for, say, a unix command like cat
or ls
?
You cannot do what you want in the general case (unbuffering after execve(2)
of arbitrary executables...)
Buffering is done by code (e.g. by some libc
code related to <stdio.h>
). And the code is defined by the program being execve
-ed.
You might perhaps play with LD_PRELOAD tricks which might call setvbuf(stdin, NULL, _IONBF, BUFSIZ);
after the execve
(but before the main
....); but this would work only with dynamically linked executables.
Perhaps using some constructor
function attribute in some initialization function of your LD_PRELOAD
-ed shared object might sometimes do the trick. Or redefine printf
, fopen
, .... in that shared object...
You commented that you do :
two-way communication with a sub-process using two pipes.
Then your approach is wrong. The parent process should monitor the two pipes, probably with a multiplexing call like poll(2), then (according to the result of that multiplexing) decide to read or to write to the child process. In reality, you want some event loop: either implement a simple event loop yourself (with e.g. poll
[iteratively called many times] inside a repeated loop) or use some existing one (see libevent or libev, or the one provided by some toolkit like GTK or Qt etc...)
You might also multiplex with select(2) but I recommend poll
because of the C10K problem
You won't lose your time by reading Advanced Linux Programming.
See also this answer to your next related question.