One place where the inconsistency gets weird is when you use signalfd with epoll. The epoll will flag events on the signalfd based on the process where the signalfd was registered with epoll, not the process where the epoll is being used. One case where this can be surprising is if you set up a signalfd and an epoll and then fork() for the purpose of daemonizing -- now you will find that your epoll mysteriously doesn't deliver any events for the signalfd despite the signalfd otherwise appearing to function as expected. That took me a day or two to debug. :(
With all that said, at the end of the day I disagree with Geoff. I would rather use signalfd than signal handlers. The "self-pipe trick" is ugly, involves a lot of unnecessary overhead, and runs the risk of deadlocking if you receive enough signals to fill the pipe buffer before you read them back (which can be solved with additional synchronization, but ick). In fact, in my own code, on systems that don't have signalfd or any similar mechanism, I tend to block signals except when I'm about to call poll(), and then siglongjmp() out of the signal handler to avoid the usual race condition. (See pselect(2) for discussion of said race condition.)
I think it's just a fact of life that you need to clear your signal mask between fork() and exec(), and yeah no one does this, whoops.
BTW, for the specific problem of dealing with child processes, I really hope Linux adopts the Capsicum interface as FreeBSD has:
https://www.freebsd.org/cgi/man.cgi?query=pdfork&sektion=2
Until then, you simply can't expect to reap children via signals. You use the signal to let you know that it's time to call wait().