If you misuse signals. I am rusty on low level programming but I have done enough to know that this poster is whining a bit too much. Signals should only be used in the general case for exceptional circumstances, like killing a programme. A signal handler's job is to deal with the crisis, e.g., gracefully exit. In lower level cases signals mean there is an urgent event, something that must be done now or it is useles…
As I mentioned in another comment, there are cases (like SIGWINCH) where the only interface the kernel gives you for general-purpose IPC is signals. In any case, if you restrict yourself to using signals for urgent respond-immediately events, then signalfd is still useless, since you want to handle those synchronously. :) (That said, I would definitely agree that the kernel is misusing signals -- SIGWINCH should just…
Signalfd is useless
31–40 of 44 posts
Re: Signalfd is useless
#32Another weirdness about signalfd: read() from a signalfd returns signals for the calling process, regardless of what process created the signalfd (e.g. it could have been inherited through fork()). That's arguably usually what you want, but is inconsistent with usual file descriptor semantics, which say that it doesn't matter who read()s. One place where the inconsistency gets weird is when you use signalfd with epol…
> 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 The unfortunate terseness of the original "self-pipe trick" description makes the solution to this difficult to see. As far as I've figured out there are two things to notice: 1) You're supposed to set the pipe to be non-blocking. Pre…
That doesn't matter. You're not supposed to have a byte in the pipe for every signal. What matters is having at least one byte any time there are unprocessed signals. The only function of the pipe is to wake the select(2) up. You still need bookkeeping elsewhere.
> That leads us to:
> 2) The self-pipe trick specifically calls out handling SIGCHLD (probably because it's one signal that you don't want to ignore!) But given the chances of dropping a byte as described in 1) and the fact that SIGCHLD and fork are explicitly called out, I can only assume that the lesson here is: only have one pipe per signal you intend to handle. Since multiple signals sent to a process may result in a single signal being delivered, your real signal handling code (the stuff that's watching the other end of the pipe) already has to deal with this situation.
Meh. Just have one pipe and a sig_atomic_t for each different types of signal you're interested in.
Re: Signalfd is useless
#33Another weirdness about signalfd: read() from a signalfd returns signals for the calling process, regardless of what process created the signalfd (e.g. it could have been inherited through fork()). That's arguably usually what you want, but is inconsistent with usual file descriptor semantics, which say that it doesn't matter who read()s. One place where the inconsistency gets weird is when you use signalfd with epol…
Does running the self-pipe trick on a separate thread solve that issue? It seems like it's basically equivalent to signalfd (neither worse nor better, unless you're worried about platform-specific thread bugs): you end up with a signal mask on your main thread, but you also avoid EINTR on your main thread. Any possible pipe lockup just happens on the signal-handling thread, so the mainloop can keep running and eventu…
There's no need for threads. Set the pipe to non-blocking and ignore the write() error if it's EAGAIN/EWOULDBLOCK. See my response above for why dropping writes if a byte already exists in the pipe is okay.
Re: Signalfd is useless
#34Re: Signalfd is useless
#35Earlier quoted context omitted.
Does running the self-pipe trick on a separate thread solve that issue? It seems like it's basically equivalent to signalfd (neither worse nor better, unless you're worried about platform-specific thread bugs): you end up with a signal mask on your main thread, but you also avoid EINTR on your main thread. Any possible pipe lockup just happens on the signal-handling thread, so the mainloop can keep running and eventu…
> Does running the self-pipe trick on a separate thread solve that issue? It seems like it's basically equivalent to signalfd (neither worse nor better, unless you're worried about platform-specific thread bugs): you end up with a signal mask on your main thread, but you also avoid EINTR on your main thread. Any possible pipe lockup just happens on the signal-handling thread, so the mainloop can keep running and even…
(But yes, setting it non-blocking is correct.)
Re: Signalfd is useless
#36Earlier quoted context omitted.
clonefd is a very limited solution. What we really need is the ability to open a file descriptor handle to any process. That ability solves all sorts of race conditions. Conveniently, we already have an interface to open file descriptors for processes: /proc. We just need to extend its semantics slightly.
Maybe I'm misunderstanding, but wouldn't opening a file descriptor to a process via /proc have the same race condition issues with process id wraparound? After all, processes in /proc are opened by process ID (the only exception I can think of is /proc/self... maybe I missed some other exceptions?) Overall, it seems easier to avoid process ID wraparound attacks via using the full 32-bit number space for PIDs. There m…
Absolutely. But once you've opened the file descriptor, the kernel would guarantee that its corresponding process ID would remain unused until you closed the file descriptor. (For example, it could keep the process a zombie if it exits.)
This way, it's possible to write a reliable killall: walk /proc, call openpid() on each entry, and with the PID FD open, examine the process's user, command line, or whatever else, kill the process if necessary, and close the process file descriptor.
No race.
Re: Signalfd is useless
#37Earlier quoted context omitted.
Maybe I'm misunderstanding, but wouldn't opening a file descriptor to a process via /proc have the same race condition issues with process id wraparound? After all, processes in /proc are opened by process ID (the only exception I can think of is /proc/self... maybe I missed some other exceptions?) Overall, it seems easier to avoid process ID wraparound attacks via using the full 32-bit number space for PIDs. There m…
> Maybe I'm misunderstanding, but wouldn't opening a file descriptor to a process via /proc have the same race condition issues with process id wraparound? Absolutely. But once you've opened the file descriptor, the kernel would guarantee that its corresponding process ID would remain unused until you closed the file descriptor. (For example, it could keep the process a zombie if it exits.) This way, it's possible to…
That seems like it would open you up to a trivial denial-of-service attack where some attacker just spawns a bunch of processes and never closes the /proc handles. Then you can't start any more processes because there are no more process IDs available. The only workaround is to have a larger PID space, which poses the question... why not just have a larger PID space in the first place and skip the new, non-portable API?
Re: Signalfd is useless
#38I'd like to see other concepts also covered by file descriptors. Example: semaphores, mutexes, condition variables, timers.
There used to be a FUTEX_FD, but it got removed. I think you can mostly achieve the effect with eventfd, though.
Re: Signalfd is useless
#39Earlier quoted context omitted.
> Maybe I'm misunderstanding, but wouldn't opening a file descriptor to a process via /proc have the same race condition issues with process id wraparound? Absolutely. But once you've opened the file descriptor, the kernel would guarantee that its corresponding process ID would remain unused until you closed the file descriptor. (For example, it could keep the process a zombie if it exits.) This way, it's possible to…
But once you've opened the file descriptor, the kernel would guarantee that its corresponding process ID would remain unused until you closed the file descriptor. (For example, it could keep the process a zombie if it exits.) That seems like it would open you up to a trivial denial-of-service attack where some attacker just spawns a bunch of processes and never closes the /proc handles. Then you can't start any more…
Re: Signalfd is useless
#40Earlier quoted context omitted.
But once you've opened the file descriptor, the kernel would guarantee that its corresponding process ID would remain unused until you closed the file descriptor. (For example, it could keep the process a zombie if it exits.) That seems like it would open you up to a trivial denial-of-service attack where some attacker just spawns a bunch of processes and never closes the /proc handles. Then you can't start any more…
It works out all right on Windows, which uses exactly the approach I advocate. And you can already DoS the system in myriad ways. If you're still worried: we have ulimits for other resources. We can have a ulimit for this one too.
I don't think a ulimit would be very effective here at preventing denial-of-service. Let's say I set it to 100... I can just have my 100 children each spawn and hold on to 100 children of their own, and so on and so forth. If I just go with a bigger process ID space all these headaches go away, plus existing software works without modification.