4 ms·
Is there any case where anything remotely related to signals is not full of perils? UNIX has plenty of rough edges, but signals must be one of the worst..
by zokier 3y ago
Is there any case where anything remotely related to signals is not full of perils? UNIX has plenty of rough edges, but signals must be one of the worst..
- deleted 3y ago[deleted]
- TeMPOraL 3y agoIs there even a way to correctly deal with signals in the first place? The more posts like this I read, the more I start to feel that there is no 100% correct way of handling signals, you can at best minimize the probability of getting hit by one at a wrong moment. Which is OK in real life, but feels bad for a fundamental part of an OS.
- CodesInChaos 3y agoI think the right choice depends on the kind of signal: 1. SigPipe and similar synchronous signals can be disabled. You already get an error code from the IO operation anyways, so they're redundant. It's kind of useful for traditional unix command line tools, since it'll just terminate the process when the output pipe gets closed. 2. SigInt and similar messages from other processes, should be handled via signalfd or the self-pipe trick. That way they look like any other asynchronous message that might come in via a pipe or socket. 3. SigSegV and other severe errors can just terminate the process Windows chose a different approach: SigSegV is a (structured) exception which causes unwinding. Unwinding has its own set of challenges, especially as language boundaries are crossed. SigInt calls a registered callback on a separate thread, so the handler is not subjected in the same limitations as a signal of an existing thread, and can use the same synchronization primitives as any other multi threaded code. GUI applications have a message queue per window, which is used for similar purposes (sometimes it makes sense for a console application to create a hidden window to receive such messages).
- pm215 3y agoThere definitely are correct patterns for handling signals -- the problem is more that there are also quite a lot of "looks plausible but has a race condition" ways to write code. The whole signals infrastructure in Unix has always been a bit dubious IMHO -- my impression from reading the Sixth Edition sources in the Lions book was that it looked like it had basically been put in to handle "signals are fundamentally fatal" cases like ctrl-C of a process from the terminal or SIGSEGV and friends, with the signal handler really only being there to do cleanup before the process exited. If that's all signals are doing then none of the race condition stuff matters (including the traditional "when a signal handler is called it resets to SIG_DFL" race that was fixed by BSD-style signals) and nor do you need to care about the enormous mountain of pain that is having to handle EINTR return values from system calls. But once the mechanism was there it got used for other things, many of which involved "the process receives and deals with signals as part of its normal continuing execution".
- DSMan195276 3y agoI think so, it's just typically a pain. For example using `ppoll()` or `pselect()` is not that bad really, but it requires you to have centered your program around such a call and have access to it (IE. It's not buried in a UI framework or network library somewhere). Beyond that, leaving signals masked for the majority of your program makes things a lot simpler (if you're multi-threaded, I'd just create a completely separate thread for signals and mask them on all the others, or use `signalfd` if you can). Handling the actual signals themselves is annoying, but in concept it's similar to any kind of async code - the only big catch is that understanding which functions you can or cannot call in a signal handler is pretty messy. Some signals also shouldn't be bothered to try and recover from, Ex. SIGSEGV. Some other signals are also just poorly designed for a multi-threaded environment and there's better alternatives to watching for them. Only some like SIGINT are really worth supporting IMO, and the signal handler should do little more than set a flag to be handled elsewhere in the regular program flow.
- matheusmoreira 3y agoNope. There is essentially no way to handle Unix signals correctly. Signals are fundamentally broken. The best way to deal with them that I've found is to turn them off by masking out every signal. That lets me reliably switch to Linux's signalfd with epoll or io_uring which finally makes it sane. The BSDs seem to have similar mechanisms.
- ajross 3y ago> Is there even a way to correctly deal with signals in the first place? There is, but it requires a level of rigor and overhead that tends to make them poor choices for practical engineering. Signals are, at their core, 1:1 isomorphic to interrupts being delivered to a physical CPU. The stop execution and transfer it to a different location at an arbitrary/uncontrollable time. Operating systems deal with this problem robustly all the time, they have to. What you typically do as the fundamental building block is provide some way of "masking" the interrupts (e.g. sigaction() with appropriate flags for every signal you might receive) so that they won't occur while you're inspecting some critical state. On hardware this is generally cheap, but with signals it's a whole system call (or many, if there are multiple signals to deal with). So generally this isn't the common technique. So you fall back to the other OS technique, typically used for non-maskable interrupts: don't touch anything. You can't call OS APIs safely (because they might have been preempted in arbitrary states), so you're limited to doing lockless algorithms that can safely update the shared data in a way that can be inspected later. Then maybe you might queue up an "event" or something to get the interrupted context to re-inspect state "soon", etc... The second option generally is what unix processes try to do. Except that it's really hard (because it's isomorphic to lockless coding!), and easy to get wrong. So signals ended up with the reuptation of being fundamentally broken. They're not. They have a home and can be used robustly. Just... don't, unless you really really need them for some reason.