3

Please, have a look at this sample code which is using a very well established programming pattern to redirect stdout to a pipe.

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();
    if (pid == 0) {
        close(fd[0]);
        dup2(fd[1], 1);
        printf("A string");
        _exit(0);
    }

    close(fd[1]);
    char text[1000];
    size_t size;
    int p = 0;
    while ((size = read(fd[0], text+p, 1)) == 1) {
        p++;
    }
    close(fd[0]);
    text[p] = '\0';
    printf("%s", text);

    return 0;
}

The code, as it is, is not working. As correctly suggested in a comment by @kaylum, calling exit instead of _exit in the child process makes the code working properly.

Zagorax
  • 11,440
  • 8
  • 44
  • 56
  • `close(fd[1])` no, `close(fd[0])` maybe? in your code – gsamaras Mar 16 '16 at 10:02
  • @gsamaras isn't `fd[0]` the reading end of the pipe which I'm supposed to use in the parent process to read from the pipe? – Zagorax Mar 16 '16 at 10:10
  • You can `close(fd[1]);`after you duplicated it, it seems a bit cleaner to me, but I doubt this will change anything to your program's behavior. – jdarthenay Mar 16 '16 at 10:20
  • What is `errno` telling you? – Daniel Jour Mar 16 '16 at 10:22
  • @DanielJour after `dup`? It says `Success`. I stripped all debugging information, but all calls lile `pipe`, `fork` and `dup2` succeed. – Zagorax Mar 16 '16 at 10:25
  • After `read` I meant. Also: `text[p+1] = "\0";` Your compiler must be spitting a warning about that line. Your probably meant to use single quotes. – Daniel Jour Mar 16 '16 at 10:28
  • Also you want to use `p` as index for the null terminator, otherwise you'll get one uninitialised element. – Daniel Jour Mar 16 '16 at 10:32
  • @DanielJour Yeah, you're right. Fixed p and single quotes. Still, after the read call errno is still set to `Success`, therefore the behaviour of the program is unchanged. – Zagorax Mar 16 '16 at 10:35
  • 2
    Call `exit` instead of `_exit`. – kaylum Mar 16 '16 at 10:37
  • @kaylum is correct. That solves it at least on my machine. Adding `fflush(stdout)` also worked, I'd assume that `exit` is doing this. – Daniel Jour Mar 16 '16 at 10:40
  • @kaylum thank you very much. That solved my problem. @DanielJour, unfortunately I had tried with `fflush(stdout)` as well and that didn't work on my machine. According to man pages, `exit` is killing the process while `_exit` is killing the calling process, but still this behaviour is unclear to me, as the parent process (if I add more code) was still being executed (and eventually hang at the read call). – Zagorax Mar 16 '16 at 10:45
  • @kaylum can you explain while `exit` instead of `_exit` works? I searched for the difference between the two, and this answer actually suggest to use `_exit` in child processes: http://stackoverflow.com/a/5423108/1433971 Thank you! – Zagorax Mar 16 '16 at 10:51
  • 2
    `_exit` may or may not flush standard IO streams (it's implementation dependent). `exit` is guaranteed to flush standard IO streams. – kaylum Mar 16 '16 at 11:10

4 Answers4

9

exit() flushes all the open streams as part its termination whereas _exit() doesn't flush - so any buffered output is lost when calling _exit().

You can make it "work" either:

1) by calling fflush(stdout); to flush the buffered output before the call to _exit() or
2) disabling the buffering for stdout with: setbuf(stdout, 0); at the beginning of the main().

POSIX requires that streams are flushed by exit():

The exit() function shall then flush all open streams with unwritten buffered data and close all open streams. Finally, the process shall be terminated [CX] with the same consequences as described in Consequences of Process Termination.

Similarly, requires that streams are not flushed by _exit():

The _Exit() [CX] and _exit() functions shall not call functions registered with atexit() nor any registered signal handlers. [CX] Open streams shall not be flushed. Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Note: Disabling buffering for `stdout` can dramatically slow performance if you're producing any meaningful amount of output. The real solution is usually "call `exit` instead"; `_exit` is only for very limited use cases. – ShadowRanger Mar 16 '16 at 11:19
  • @ShadowRanger There are cases where `_exit()` is useful (such as creating a daemon) or when you don't want the exit handlers to run when forking etc. So, what's ideal (`exit()` or ``fflush(stdout)` + `_exit()`) depends on the situation. But I agree disabling the buffering is the least preferred/useful option (except, may be, for "realtime" output -- otherwise, where a repeated call to fflush would be needed). – P.P Mar 16 '16 at 11:34
2

_exit terminates the process without performing the usual cleanup steps that exit (or returning from main) does. It's suggested for use in child processes for certain circumstances because it avoids performing incorrect or unnecessary cleanup; when forking a worker to perform a short task, the parent task might have unflushed I/O buffers, and using _exit prevents the same buffered data from being written by both the parent and the child, mysteriously duplicating output. It also prevents atexit handlers and the like from executing.

In a C++ program, it also bypasses stack unwinding, so RAII cleanup (destructor invocation) doesn't occur either; mostly meaningless for purely local resources (the child exits a little quicker, but that usually doesn't matter), but important if the cleanup affects externally visible state.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
2

exit is a C-level function that flushes and then closes every C-level streams (stdin, stdout and stderr are C-level streams).

_exit is a POSIX-level function that closes every system-level streams (descriptors like 0, 1, 2, etc are system-level streams). In this case, buffering is an system internal concern.

You've made a redirection to let printf (C-level function) finally writes to descriptor 1 (system-level), but printf buffers while buffer is not full and the C-string does not contains \n (it is your case). Then terminating by a call to _exit will simply lost the unflushed buffer content.

On POSIX systems, exit flushes every C-stream and then call _exit (which in turn closes every system-stream before really terminating the execution).

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
0

Usually _ is reserved for implementations. _Exit was introtuced with C99, so you probably have to use a switch to make your coompiler conform to C99.

Friedrich
  • 5,916
  • 25
  • 45