Hacker News new | ask | show | jobs
by rwl4 3636 days ago
Signals are almost the app version of interrupts which is a fundamental way the peripherals (and your programs!) communicate with the kernel.

Sure handling them can be a pain in the ass, but I'd argue that signals are beautiful in their simplicity and extremely powerful. They are used for far more than signaling hang up and termination.

4 comments

Signals are an incredibly brittle interface. Not only can you lose signals all the time (if a signal is sent to a process while it is handling another signal then you can lose that signal), but you lose a lot of semantic information because the model of signal is "random callback that you jump to and you lose your old stack state while in the signal" is incredibly lossy. There's a lot of other problems (signals not being queueable, as well as the fact that signals expose a bunch of kernel race conditions by design) but you get the gist.

Unix did a lot of things right, but signals was certainly not one of them. Sometimes the simple ideas aren't the best ones.

> if a signal is sent to a process while it is handling another signal then you can lose that signal

> signals not being queueable

Are you sure? I'm not an expert on signal handling, but I believe these statements are not true in the case of signalfd(2), which allows you to read signals from a file descriptor and does not require a signal handler.

Signal sending cannot block, so you'd need to have an unlimited amount of memory in order to guarantee that all signals are delivered. Not to mention that some signals allow you to block a process in the middle of a syscall (resulting in the lovely restart_syscall and EINTR hacks).

Also, signalfd(2) has its own lovely host of problems (caused by the fact that a file descriptor and signals don't match in abstractions). On fork(), sendmsg() or exec() or any other interesting event, the file descriptor starts to lean towards breaking POSIX. In addition, because of signals' interactions with threads, you get some even odder interactions when you read from the fd in a multithreaded program (they share the file descriptor table but will read different data).

It is necessarily true because sending signals cannot block and queueing an unbounded number of signals would require unbounded amounts of memory.
> signals not being queueable

real-time signals are queueable.

Signals seem like the kind of thing one would inflict on userspace after having had to deal with interrupts in the kernel, yes. "Share in my pain, userspace programmers!"

A standard mechanism needs to exist to send a message to an arbitrary process for things like "please gracefully terminate", but I think the ideal interface looks like a combination of 1) signalfd (minus some of the legacy signal-specific bits), for any signals the process can handle, and 2) an in-kernel mechanism to SIGKILL, SIGSTOP, or SIGCONT processes, which the process can't do anything about anyway.

(And things like SIGWINCH should never have been signals; those should just be a standard message associated with the terminal device itself.)

CPU interrupts aren't too bad in a kernel, because a kernel is built around the idea of frequently switching task, so one more kind of switching isn't a big deal. Also, when the kernel asks the CPU to do something (like write to memory), the CPU will generally wait until the task is complete, or reaches some convenient checkpoint, before stopping and jumping to the interrupt vector.

On the other hand, most programs aren't designed for context switching, so the design overhead of dealing with signals is significant. Furthermore, signals can arrive at any time, even during a syscall, and while it's possible to do reliable I/O in the face of random interruptions, it's quite tricky.

> but I'd argue that signals are beautiful in their simplicity and extremely powerful.

There's nothing beautiful when you have a MT program which also has to handle signals for various reasons. Signals are an abomination, not the least because a signal handler is global for the whole process.