User: Password:
|
|
Subscribe / Log in / New account

Ghosts of Unix past, part 3: Unfixable designs

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 16, 2010 16:43 UTC (Tue) by foom (subscriber, #14868)
Parent article: Ghosts of Unix past, part 3: Unfixable designs

> signalfd

Unfortunately, signalfd has a very irritating practical issue.

To use it, you need to block the signal you're interested in (using e.g. sigsetmask). However, the set of blocked signals is not reset by exec (blocked signals and signals set to SIG_IGN are preserved, but other signal actions are reset to default). So, if you use signalfd, whenever you spawn a process, it will not receive that signal. And processes tend to misbehave when not receiving signals they expect to.

You can, of course, fix that. You simply need to unblock the signal after forking, but before exec'ing. *IF* you control everything that ever calls fork/exec from your process. In many situations, that is impossible -- programs tend to use all sorts of libraries, some of which spawn processes.

Okay, so, you might say: "Hey, that's what pthread_atfork is for! Just set an child-side after-fork handler to unblock the signal". Well, unfortunately, pthread_atfork doesn't always get called when spawning a child process, so you can't really use it for that.

Three examples of that:
1) For the system() call, POSIX says: "It is unspecified whether the handlers registered with pthread_atfork() are called as part of the creation of the child process." In glibc, they aren't.
2) Regarding posix_spawn, POSIX says: "It is implementation-defined whether the fork handlers are run when posix_spawn() or posix_spawnp() is called." In glibc, they are.
3) The linux-specific clone() system-call does not have atfork handlers called.

So, basically, end result: signalfd is unusable in many circumstances where it'd be really nice to be able to use it -- you're better off just setting a standard signal handler which writes to an fd. Sigh.

(POSIX spec URL: http://www.opengroup.org/onlinepubs/9699919799/)


(Log in to post comments)

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 16, 2010 17:17 UTC (Tue) by michaeljt (subscriber, #39183) [Link]

Wouldn't a named pipe in the filesystem do as well in many of the cases where signalfd is a feasible solution? Presumably we are looking at the more specialised cases of signal handling here which are likely to be application-specific protocols, and not just handling SIGINT.

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 16, 2010 17:29 UTC (Tue) by foom (subscriber, #14868) [Link]

I think you've got the wrong idea. Signalfd isn't intended for "specialized" applications of signals (and I'm not sure how named pipes come into play at all). It would be nice to use for completely normal uses of signal handling: for example, it would be ideal for replacing a SIGCHLD handler in an application with an event loop: you're already waiting on fds to become readable/writable, so waiting on a signalfd fd to notify you of a child that finished is exactly what you want.

Most sensible such applications will already implement that by writing a signal handler for SIGCHLD which simply writes a byte into a pipe, and then has the event loop look for readability on that pipe. Signalfd would let you do that more easily -- if you could actually use it.

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 16, 2010 17:33 UTC (Tue) by michaeljt (subscriber, #39183) [Link]

> Signalfd isn't intended for "specialized" applications of signals (and I'm not sure how named pipes come into play at all). It would be nice to use for completely normal uses of signal handling: for example, it would be ideal for replacing a SIGCHLD handler in an application with an event loop [...]

That makes sense - and obviously a named pipe would be no good there whatsoever. I was more thinking of things like SIGUSR1 sorts of interactions.

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 17, 2010 8:11 UTC (Wed) by nix (subscriber, #2304) [Link]

It seems like what you want is... another arbitrary patch atop the mess! Specifically, a rule that unblocked signals which have open signalfds act exactly as if blocked (pending signals being sent down the signalfd 'instead' of being conventionally delivered) until the fd is closed. Now, assuming that the user has opened the signalfd O_CLOEXEC (hey, that should be the default! but we repeat ourselves), the signal will automatically 'unblock itself' at exec() time, which is exactly what we want.

Bonus: no change to signal semantics when signalfd is not in use, and nobody sane would want the current semantics in any case.

What am I missing?

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 17, 2010 14:43 UTC (Wed) by madcoder (subscriber, #30027) [Link]

No, you want a real atfork() interface that is not related to threads. pthread_atfork is here because when you fork() from threads, you fork one new process, not "all the threads" and that often means that you have to deregister stuff.

Anyway, there is a solution for that (which is messy butÂ…) on linux which is to redefine fork(), system(), pthread_spawn{,p} and every similar problematic fork() wrapper using dlsym chaining to reset your signal masks properly. This isn't *that* complicated, and chains nicely. Or if you're sure that pthread_atfork() works for some then only divert the ones where it doesn't. I know it's not portable but signalfd() isn't in the first place either ;)

WRT clone() I'd say that this is a very low level interface which has a really high chance to break the libc when used (e.g. TSD breaks in interesting ways in the glibc if you use clone without emulating what the glibc does IIRC), so I'd say people using it Know What They Are Doing in the first place and should have worried about resetting the signal mask to a sane default in the first place.

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 24, 2010 22:04 UTC (Wed) by neilbrown (subscriber, #359) [Link]

I was wondering if you had brought this up with the developer of signalfd - Davide Libenzi?

Fixing it would probably require adding a new 'flags' option, so adding a new syscall and deprecating the old. This 'flags' could allow atomic setting of close-on-exec and an auto-block flag which causes all signals being tracked by signalfd to blocked just as long as the signalfd is open.

If you haven't and don't want to, I might....

Thanks,
NeilBrown

Ghosts of Unix past, part 3: Unfixable designs

Posted Nov 25, 2010 18:28 UTC (Thu) by dcoutts (guest, #5387) [Link]

Please do bring this up with the kernel hackers. We were thinking of using signalfd in the GHC runtime / IO system until we discovered this problem with having to block all signals in all threads which makes it unusable (it's not just child processes, libraries can make their own threads). We have to stick with the approach of installing a signal hander that writes to a pipe (or we can use eventfd for the cases where there is no data associated with the signal).

Ghosts of Unix past, part 3: Unfixable designs

Posted Apr 14, 2016 7:01 UTC (Thu) by linuxrocks123 (subscriber, #34648) [Link]

Can't you #include <dlfcn.h> and intercept calls to glibc's fork()?


Copyright © 2017, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds