Show Source

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.