Recurse Center lab notes 2015-06-29: fun with signals; confusing shell-writing
Signals
I finished skimming the chapter on signals in Advanced Programming in the UNIX Environmnent. This took me a few attempts over the last few days, and I fell asleep reading it at least once. One big takeaway for me was the distinction between synchronous and asynchronous signals. In a nutshell:
- synchronous signals
- these signals are a direct result of execution; examples include
SIGFPE
(stands for floating ppoint exception), which is sent on various errors in mathematical functions, such as dividing by zeroSIGPIPE
(stands for… pipe), which is sent if there’s a write to a pipe with no readersSIGSEGV
(stands for segmentation violation), which is sent to a thread if it tries to access memory in a way it’s not allowed to, eg, dereferencing a null pointer, reading read-protected memory like kernel memorySIGBUS
(bus error, whatever that is meant to mean1!), which is sent on some types of memory access issues, eg, improper alignment, or—the one that’s exciting to me—attempting to access memory mapped beyond the end of a memory mapped file2
- asynchronous signals
-
these signals can get sent independently of the running process or thread; they just show up. Examples include: -
SIGINT
(interrupt), which is sent when you interrupt a process from the keyboard with^C
at the terminal -SIGKILL
, which is sent when youkill -9
something to kill it with fire; the process terminates, and the signal cannot be caught, blocked or ignored -SIGSTOP
, which is sent when you stop a process with^Z
at the terminal; the process is stopped, and will continue when it’s sent aSIGCONT
(continue) -SIGHUP
(hangup), which is sent if the controlling terminal ‘hangs up’, ie, is closed—this one is a fun anachronism which serves to us that we’re still using programs that pretend to be terminals, which are in turn mostly pretending to be teletype machines, and that everything sort of pretends to be connected over a phone line
Python shell writing weirdness
Sophie asked me for some help with the mental model for file descriptors and
pipes and dup2
and what happens when you fork and other such fun things. There
is a really strange bug we couldn’t figure out: ls | wc
or ls | head
would
terminate as expected, but yes | wc
or yes | head
would not; yes
would
just keep on running and use loads of CPU, so it was not idle. On OS X only (2
machines tested). On Linux, it worked as expected, with yes
terminating and
complaining about the broken pipe. We gave up eventually, and I still haven’t
got any idea what’s wrong. I really wish procfs
existed on OS X!
I wrote a minimized equivalent of our code. Please run it and let me know what happens! On my machine:
$ curl https://gist.githubusercontent.com/kamalmarhubi/65c2ea1063e479f0c16b/raw/2459725829891b156f61ec5fc808587846ec07be/pipe_weirdness.py > pipe_weirdness.py
$ chmod +x pipe_weirdness.py
$ ./pipe_weirdness yes head
y
y
y
y
y
y
y
y
y
y
yes: standard output: Broken pipe
yes: write error
$ █
I’m really interested in ideas on why yes
wouldn’t terminate!
-
I think this is meant to be related to the memory bus, which makes sense for unaligned accesses, but not for the memory mapped file situtuation. ↩
-
From man pages and some experimentation,
SIGBUS
is sent for this situation on Linux, FreeBSD, OS X, and NetBSD; OpenBSD’s documentation says aSIGSEGV
is sent instead. Feel free to test it out on your system! On mine:$ curl https://gist.githubusercontent.com/kamalmarhubi/d8a3b3754bc2f4f62899/raw/333a1c8ebce4a3c7718e740ea4d175d22ca206fd/mmap_signal_test.c | cc -xc -o mmap_signal_test - $ ./mmap_signal_test Received signal while accessing 0x7facd3268000: Bus error Going to ftruncate Read: 0 Wrote and read: 1 $ █