How old various Unix signals are

October 7, 2022

Process signals are a famously tricky part of Unix in practice (although they can sometimes seem simple in theory). Recently I found myself wondering how old various Unix signals are. The answer turns out to be older than I expected for most signals, although it's difficult for me to tell exactly how far back they really go because of our limited sources for very early versions of Research Unix.

We have relatively good information as far back as V4 (Fourth Edition), the first version of Unix that was written in C. That version has a signal.2 manpage, which lists 12 signals (from 1 to 12). The low numbered signals are the same as they are today, which at least suggests that they date back pretty far. And apart from signal 9 (SIGKILL), signals from 4 (SIGINS, illegal instruction) through 12 (SIGSYS, bad argument to system call) all seem aimed at telling you about specific internal program issues instead of being something that you would actively send. V4 also has a kill.2 manual page. The actual shorthand signal names are defined in the kernel's param.h.

Third Edition (V3) has a kill.2, which takes as an argument only the process to terminate and force a core dump for (what we would now call SIGQUIT). It doesn't have a signal(2) manual page, but it does have an intr.2 manual page, which describes a system call that controls what we would now call SIGINT. Given the history of process groups and signals, I suspect that V3 also had a version of SIGHUP as well. This would cover the first three signal numbers in V4. Since V3 clearly had core dumps, it's possible that it also had some of V4's program trap signals, although I suspect that it only had up to signal 8 at most, since it wouldn't have needed SIGKILL (9) since kill.2 doesn't seem to have been blockable. So it's possible that SIGKILL (9), SIGBUS (10), SIGSEG (11), and SIGSYS (12) were all added in V4.

I don't think we have V5 manual pages, but we do have the V5 kernel param.h, which defines the signal numbers, which are the same as in V4. We have the V6 signal.2, which adds SIGPIPE (13). However, V6 also raises 'NSIG' to 20 (in param.h), which has the effect of allowing you to send and catch unnamed signals from 14 through 19. I don't know if anything in V6 actually used this capability.

The V7 signal.2 adds SIGALRM (14) and SIGTERM (15), and lists 16 as 'unassigned'. The signal definitions are now available in /usr/include/signal.h, and NSIG has dropped to 17, meaning that signal 16 is the only unnamed signal that can be sent and received by processes. However the kernel has its own version in h/param.h, which contains the illuminating comment:

 * No more than 16 signals (1-16) because they are
 * stored in bits in a word.

(V7 also uses different names for some signals between the two definitions. The kernel #defines are almost all six characters long, so we have things like 5 is 'SIGTRC' instead of 'SIGTRAP' and 14 is 'SIGCLK' instead of 'SIGALRM'.)

The V7 method of storing a process's pending signals is different from V6's. The V6 proc.h has a 'char p_sig' for '[the] signal number sent to this process'. The V7 proc.h changes this to a 'short p_sig' of 'signals pending to this process'. Without checking the kernel code, I expect that V6 could only have one signal pending to a process at a time, while V7 allows a bitmask of multiple pending signals.

The System III signal.2 defines signal 16 as SIGUSR1 and adds SIGUSER2 (17), SIGCLD (18), and SIGPWR (19). The 4BSD sigsys.2j is even more expansive, as you might expect from the addition of job control. It defines signals up through 25 (SIGXFSZ), and has a SIGCHLD (20), but leaves signal 16 undefined. In the 4.2BSD sigvec.2, signal 16 becomes used for SIGURG ('urgent condition present on socket'), and signals are defined up through signal 27 (SIGPROF). The 4.3BSD sigvec.2 finally adds SIGUSR1 and SIGUSR2, along with SIGWINCH, taking 4.3 BSD up to 31 signal numbers (although 29 appears unnamed). After that I suspect things become rather sprawling, and it's possible that by 4.3 BSD, some of its signals had first appeared in BSD derivatives like SunOS.

POSIX has a required set of signal names in <signal.h>. Naturally it doesn't require any specific signal numbers for them. If I'm counting right, it has 28 signal names. I will leave it to other people to identify 4.3 BSD signals (or modern signals) that aren't in POSIX, because by the time we've gotten to POSIX we're long out of Unix's early history.

Written on 07 October 2022.
« My performance intuitions and the complexities of SSD performance
Linux NFS clients (normally) make only one TCP connection to each fileserver »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Fri Oct 7 23:04:19 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.