Yesterday Reyk fixed a tiny bug in vmd. It wasn’t possible to kill the process by pressing ^C. As explained in the commit, the accept4
system call was being restarted after the signal.
By default, most signal handlers that a program establishes have the SA_RESTART
flag set, which causes an interrupted system call to be restarted. Actually, by default signals are either ignored or cause the program to terminate, so this isn’t a problem at all, but any handler installed by calling signal
sets this flag. More control over signal actions is possible using the appropriately named sigaction
function.
On the kernel side, system calls that need to block call tsleep
which usually waits for a corresponding wakeup
or a timeout to expire. However, it may also return an error (ERESTART
) if it’s interrupted by a signal. Most system calls don’t inspect the error code, they simply pass it along. But when the kernel is about to return to userland, it will notice this error code and run the syscall again.
Among the system calls that handle ERESTART specially are poll
, select
, and kevent
. All of them check for ERESTART and immediately map it to EINTR so it gets returned to userland.
Back in vmd, it had installed a SIGTERM handler that set a quit flag variable to true, but was getting stuck in accept. The code looking at quit never had a chance to run. The fix was to add a call to poll before accept.
Most of the time the default behaviors make sense. Restarting system calls prevents a lot of spurious failures from propagating. Applications that install signal handlers usually use one of the interruptible functions in the core of their event loop. vmd happened to be an exception, that only needed to handle one event and tried to take a shortcut.
Additional commentary about history of EINTR.
Posted 24 Nov 2015 17:52 by tedu Updated: 20 Jun 2019 01:08
Tagged:
c openbsd programming