What Exactly Does Control-D Mean?¶
(Originally posted as part of an answer to a student question in JHU’s Compilers class, spring 2015.)
The UNIX terminal layer is a beast, but the relevant bits here are that, in
canonical processing mode, EOT (aka EOF aka ^D) is not passed through to the
program: you can verify this by running cat | od -ha
and typing, for
example, asdf^Dfdsa^D^D
. Instead, EOT directs the terminal layer to
segment the input stream and un-block any blocked read() calls even if they
are not yet “full”. For excruciating detail, see
http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html and
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap11.html (or
don’t, if you’d rather, which is just fine by me). (To see just how
different things are with “canonical” mode off, run something like stty
-icanon; cat
. Note that at least my shell resets icanon whenever it
displays a prompt, so it won’t work to run the commands separately.)
The UNIX convention, then, is that a read returning 0 indicates a transient
condition of being at (or past!) the end of the file. UNIX cat chooses to
interpret this as a permanent condition and exit, because that’s almost
always what you want. (But see things like stdio
’s clearerr()
call!)
This is why cat exits after newline-then-^D or two ^Ds back-to-back:
ultimately, a read()
call returned 0.
How Does Glibc Flush My Streams At Exit?¶
(Originally posted as part of an answer to a student question in JHU’s OS class, fall 2015.)
In the case of the GNU libc (aka glibc
), its documentation
http://www.gnu.org/software/libc/manual/html_node/Flushing-Buffers.html says
that “When the program terminates by calling exit” a flush will happen. On
the other hand,
http://www.gnu.org/software/libc/manual/html_node/Termination-Internals.html
rightly says that when a program exits streams are not flushed! So what
gives?
What gives is that glibc is doing something nice for you that it doesn’t
have to. The portable way to do this is to call fflush(stdout);
fflush(stderr);
(etc) before exiting your program, or to otherwise ensure
that flushes have taken place (by, e.g., printing a newline with
newline-induced flushing turned on).
In glibc
, the thing that ties together all the pieces for flushing
streams at exit is the invocation of the text_set_element
macro at
http://osxr.org/glibc/source/libio/genops.c?v=glibc-2.17#1340 . But all that
does is create a C symbol in the __libc_atexit
section of the library
file (you can verify that this section exists with something like objdump
-x /lib/libc*.so
)… the run-at-exit functionality begins at
http://osxr.org/glibc/source/stdlib/exit.c?v=glibc-2.17#0026 using some dark
ELF loader dragons (as in “here be…”); the close-everything-down logic
begins at http://osxr.org/glibc/source/libio/genops.c?v=glibc-2.17#1006.