Reputation: 1130
I am wondering why uncommenting that first printf statement in the following program changes its subsequent behavior:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
//printf("hi from C \n");
// Close underlying file descriptor:
close(STDOUT_FILENO);
if (write(STDOUT_FILENO, "Direct write\n", 13) != 13) // immediate error detected.
fprintf(stderr, "Error on write after close(STDOUT_FILENO): %s\n", strerror(errno));
// printf() calls continue fine, ferror(stdout) = 0 (but no write to terminal):
int rtn;
if ((rtn = printf("printf after close(STDOUT_FILENO)\n")) < 0 || ferror(stdout))
fprintf(stderr, "Error on printf after close(STDOUT_FILENO)\n");
fprintf(stderr, "printf returned %d\n", rtn);
// Only on fflush is error detected:
if (fflush(stdout) || ferror(stdout))
fprintf(stderr, "Error on fflush(stdout): %s\n", strerror(errno));
}
Without that first printf, the subsequent printf rtns 34 as if no error occured even though the connection from the stdout user buffer to the underlying fd has been closed. Only on a manual fflush(stdout) does the error get reported back. But with that first printf turned on, the next printf reports errors as I would expect. Of course nothing is written to the terminal(by printf) after the STDOUT_FILENO fd has been closed in either case.
I know it's silly to close(STDOUT_FILENO)
in the first place here; this is an experiment I stumbled into and thinking someone more knowledgeable in these areas may see something instructive to us in it..
I am on Linux with gcc.
Upvotes: 4
Views: 1144
Reputation: 133978
If you strace
the both programs, it seems that stdio
works so that upon first write, it checks the descriptor with fstat
to find out what kind of file is connected to the stdout
- if it is a terminal, then stdout
shall be line-buffered, if it is something else, then stdout
will be made block-buffered. If you call close(1);
before the first printf
, now the initial fstat
will return EBADF
and as 1
is not a file descriptor that points to a character device, stdout
is made block-buffered.
On my computer the buffer size is 8192 bytes - that many bytes can be buffered to be written to stdout
before the first failure would occur.
If you uncomment the first printf
, the fstat(1, ...)
succeeds and Glibc detects that stdout
is connected to a terminal; stdout
is set to line-buffered, and thus because printf after close(STDOUT_FILENO)\n
ends with newline, the buffer will be flushed right away - which will result in an immediate error.
Upvotes: 4