Re: SIGCHLD and sigaction()
On 19.08.2020 20:02, Roy Marples wrote: > On 18/08/2020 20:52, Mouse wrote: Perhaps it would need a new flavour of file descriptor, [...] >>> Linux has apparently done this: pidfd (file descriptors representing >>> a process). The idea is that you can pass them to various system >>> call variants that otherwise take pids, without the risk that the >>> process has exited in the mean time and the pid re-used. >> >> I've been thinking about something like that myself, starting with >> AF_PID sockets, then deciding they wouldn't/couldn't work (as think I >> mentioned in this thread, the socket infrastructure really wants the >> contents of a socket to be independent of who's accessing it). >> Personally, I've wanted it as a way to provide an out-of-band channel >> to userland programs (like a control command channel for various >> daemons), but...hmm. >> >> Feels strange to find an idea I like coming from Linux. > > You can relax. > FreeBSD did it first for Capsicum. > > https://www.freebsd.org/cgi/man.cgi?query=pdfork&sektion=2 > > Roy Hi, I have got a draft work on this. It's composed of: 1. New id_t type in P_PIDFD. 2. sigsendset(2) + sigsend(3), picked from SVID (Solaris, etc) It's a generalized version of kill(2). It allows specifying P_PIDFD. 3. waitid supporting new id type: P_PIDFD. Linux is close to the above, except reinventing the wheel instead of picking sigsendset(2) + integration with /proc. FreeBSD went with a distinct direction and invented new semantics of process file descriptors, that differs to references over pid_t. For example whenever you close(2) a file descriptor, it kills the process. You also must wait for process events in FreeBSD using kevent(2). This introduced incompatibilities with the established UNIX semantics. Linux went a different path and whenever a process dies, we get ESRCH, which is sensible as it avoids e.g. killing a random process with recycled pid_t. We want process file descriptors to reference process handles through PID namespaces, for hopefully upcoming support of containers. signature.asc Description: OpenPGP digital signature
Re: SIGCHLD and sigaction()
On 18/08/2020 20:52, Mouse wrote: Perhaps it would need a new flavour of file descriptor, [...] Linux has apparently done this: pidfd (file descriptors representing a process). The idea is that you can pass them to various system call variants that otherwise take pids, without the risk that the process has exited in the mean time and the pid re-used. I've been thinking about something like that myself, starting with AF_PID sockets, then deciding they wouldn't/couldn't work (as think I mentioned in this thread, the socket infrastructure really wants the contents of a socket to be independent of who's accessing it). Personally, I've wanted it as a way to provide an out-of-band channel to userland programs (like a control command channel for various daemons), but...hmm. Feels strange to find an idea I like coming from Linux. You can relax. FreeBSD did it first for Capsicum. https://www.freebsd.org/cgi/man.cgi?query=pdfork&sektion=2 Roy
Re: SIGCHLD and sigaction()
>> Perhaps it would need a new flavour of file descriptor, [...] > Linux has apparently done this: pidfd (file descriptors representing > a process). The idea is that you can pass them to various system > call variants that otherwise take pids, without the risk that the > process has exited in the mean time and the pid re-used. I've been thinking about something like that myself, starting with AF_PID sockets, then deciding they wouldn't/couldn't work (as think I mentioned in this thread, the socket infrastructure really wants the contents of a socket to be independent of who's accessing it). Personally, I've wanted it as a way to provide an out-of-band channel to userland programs (like a control command channel for various daemons), but...hmm. Feels strange to find an idea I like coming from Linux. /~\ The ASCII Mouse \ / Ribbon Campaign X Against HTMLmo...@rodents-montreal.org / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
Re: SIGCHLD and sigaction()
On Sat 15 Aug 2020 at 16:46:13 -0400, Mouse wrote: > Personally, I don't like it; I think signals should be much like > hardware interrupts in that a second instance happening before the > first is serviced gets silently merged. If you want some sort of > queued notification of child death, it seems to me a much righter, much > more UNIXy, tack to take is to add something like AF_CHILD sockets or > some such and get child death notifications that way. (Actually, I'm > not sure sockets would work without severe hackery; too much of the > socket machinery assumes the data in a socket is independent of who > reads from it. Perhaps it would need a new flavour of file descriptor, > akin to kevent or timerfd descriptors. Linux has apparently done this: pidfd (file descriptors representing a process). The idea is that you can pass them to various system call variants that otherwise take pids, without the risk that the process has exited in the mean time and the pid re-used. See https://man7.org/linux/man-pages/man2/pidfd_open.2.html Personally I'd think you should be able to get pidfds from the /proc file system, but here Linux seems to have missed a chance. -Olaf. -- Olaf 'Rhialto' Seibert -- rhialto at falu dot nl ___ Anyone who is capable of getting themselves made President should on \X/ no account be allowed to do the job. --Douglas Adams, "THGTTG" signature.asc Description: PGP signature
Re: SIGCHLD and sigaction()
>> I don't understand what problem queued SIGCHLD was invented to >> address. > My impression is that it allows you to get notified of state changes > of your child processes. If one signal could annonce several state > changes, how would you know what these state changes are? You'd call wait4(2) (or waitpid or wait3) with WNOHANG until it returned 0 (or returned -1 with ECHILD), collecting one child status change each time. You'd still need to do more or less the same with queued SIGCHLD; the only difference is it would let you skip the WNOHANG and call it exactly once per signal. (Unless a single child changed state twice, such as by being stopped and then killed, in which case the implementation had better queue wait stati as well or you'll be calling wait too often!) /~\ The ASCII Mouse \ / Ribbon Campaign X Against HTMLmo...@rodents-montreal.org / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
Re: SIGCHLD and sigaction()
> I don't understand what problem queued SIGCHLD was invented to address. My impression is that it allows you to get notified of state changes of your child processes. If one signal could annonce several state changes, how would you know what these state changes are?
Re: SIGCHLD and sigaction()
>>> When I install a SIGCHLD handler via sigaction() using SA_SIGINFO, >>> is it guaranteed that my handler is called (at least) once per >>> death-of-a-child? >> "Maybe." It depends on how portable you want to be. >> [...] > While we're on this topic. Unix signals don't exactly work like > hardware interrupts anyhow, I suspect, and it's a thing that have > constantly befuddled me. (Caveat: this is all just my understanding. I think it's accurate, but I welcome corrections.) No, they don't. To the extent that they do, they work like (latched) edge-triggered interrupts. There is no signal analog to a level-triggered interrupt. And, as you say, there are some important differences even beyond that. > As far as I can tell, there is a problematic race condition in the > old signal mechanism, and that is the reason (I believe) why the new > semantics were introduced). I think you are right. But it's not just "old" and "new". There are really old signal semantics, where the handler is uninstalled, I think it is, when the signal is delivered, and it's up to the code to reinstall it. This is vulnerable to, if nothing else, the race where a second signal arrives before the handler gets reinstalled, though that's less important for SIGCHLD (see below). (There may be an even older form of signal handling, but if so I know nothing about it.) This then led to (so-called) reliable signals, where the handler stays installed (or at least can be set to do so) but signals can be blocked, and get blocked (or, again, at least can be set to be) when the handler is called. However, there is still only a single pending bit per signal. I think these came in sometime in the 4BSD era, but I don't recall details - indeed, I'm not sure I ever knew details. Turning SIGCHLD into something queued, something more like bytes in a pipe, is, in my perception, more recent yet. I'm not sure who invented them (but see below). There are traces of the first form in modern NetBSD, in the form of the SA_RESETHAND bit. I don't *think* that gives full unreliable-signal semantics, but I haven't checked in enough detail to be sure - I think you'd also need an SA_DONTBLOCK flag or something of the sort, or the handler would have to explicitly unblock the signal. > You have two child processes. One exit, and you get into your signal > handler. In there you then call wait to reap the child and process > things. You then call wait again, and repeat until there are no > children left to reap, as you only get one signal, even if you get > multiple children that exits. When no more unreaped children exist, > you exit the signal handler, and a new signal can be delivered. Right. > However, what happens if the second child exists between the call to > wait, and the exit from the signal handler? It would seem the signal > would get lost, since we are in the process of handling the signal, > and a new signal is not delivered during this time. If you're using unreliable signals - the first sort I outlined above - then yes, there is either this race, or, if you reinstall the handler before you do your last wait call, a different race. (Provided your handler doesn't mind being called recursively between reinstall and return, this may be tolerable.) If you're using reliable signals but without queued SIGCHLD, this is not a problem, because the second SIGCHLD (and any additional later SIGCHLDs) will set the pending bit for SIGCHLD. As soon as you return from the handler (or explicitly unblock the signal), it will be delivered. > Now, have I misunderstood something about how non-queued signal > handling works, or is/was there a problem there? There was, but it was a problem with the oldest of the above three kinds of signal. Queued SIGCHLD was not necessary for it - I don't understand what problem queued SIGCHLD was invented to address. The only thing I can think of was that it came from SRV4, which had a NIH attitude towards BSD reliable signals, so they invented queued SIGCHLD to get reliable child death handling, and then someone (POSIX maybe?) decided it would be good to include all of both mechanisms. /~\ The ASCII Mouse \ / Ribbon Campaign X Against HTMLmo...@rodents-montreal.org / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
Re: SIGCHLD and sigaction()
On 2020-08-16 12:49, Johnny Billquist wrote: On 2020-08-15 22:46, Mouse wrote: When I install a SIGCHLD handler via sigaction() using SA_SIGINFO, is it guaranteed that my handler is called (at least) once per death-of-a-child? "Maybe." It depends on how portable you want to be. Historically, "no": in some older systems, a second SIGCHLD delivered when there's already one pending delivery gets, lost same as any other signal. Then someone - POSIX? SVR4? I don't know - decided to invent a flavour of signal that's more like writes to a pipe: multiple of them can be pending at once. Some systems decided this was sane and implemented it. Personally, I don't like it; I think signals should be much like hardware interrupts in that a second instance happening before the first is serviced gets silently merged. While we're on this topic. Unix signals don't exactly work like hardware interrupts anyhow, I suspect, and it's a thing that have constantly befuddled me. As far as I can tell, there is a problematic race condition in the old signal mechanism, and that is the reason (I believe) why the new semantics were introduced). The problem goes like this: You have two child processes. One exit, and you get into your signal handler. In there you then call wait to reap the child and process things. You then call wait again, and repeat until there are no children left to reap, as you only get one signal, even if you get multiple children that exits. When no more unreaped children exist, you exit the signal handler, and a new signal can be delivered. However, what happens if the second child exists between the call to wait, and the exit from the signal handler? It would seem the signal would get lost, since we are in the process of handling the signal, and a new signal is not delivered during this time. In real hardware this usually don't happen, because the actual interrupt request can be reissued by the device while you are in the interrupt handler. There are some hardware interrupt designs, with edge triggered interrupts, where similar problems can exist, and those you have to be very careful with how you handle them so you don't get to the same kind of race condition. Now, have I misunderstood something about how non-queued signal handling works, or is/was there a problem there? Reading the current documentation, I would assume that at the call to the signal handler, the signal is blocked, and also removed from pending signals, so a new even would queue up a new signal to be delivered when returning from the signal handler. However, the text above is from trying to recall how it used to be going back in time, to when you had to re-install the signal handler after each activation. I can't seem to find documentation for how it worked back in the day. I can't even remember when/where I was reading up on that and thinking there might be a problem here, but it was a long time ago. So this is possibly just of historical interest. By the way, I haven't seen any explicit mention of the pending signal being cleared at signal handler entry, so that is just my assumption right now. If that is wrong, then I would expect there is a race condition in there. Maybe someone else knows where that detail is documented? Johnny -- Johnny Billquist || "I'm on a bus || on a psychedelic trip email: b...@softjar.se || Reading murder books pdp is alive! || tryin' to stay hip" - B. Idol
Re: SIGCHLD and sigaction()
On 2020-08-15 22:46, Mouse wrote: When I install a SIGCHLD handler via sigaction() using SA_SIGINFO, is it guaranteed that my handler is called (at least) once per death-of-a-child? "Maybe." It depends on how portable you want to be. Historically, "no": in some older systems, a second SIGCHLD delivered when there's already one pending delivery gets, lost same as any other signal. Then someone - POSIX? SVR4? I don't know - decided to invent a flavour of signal that's more like writes to a pipe: multiple of them can be pending at once. Some systems decided this was sane and implemented it. Personally, I don't like it; I think signals should be much like hardware interrupts in that a second instance happening before the first is serviced gets silently merged. While we're on this topic. Unix signals don't exactly work like hardware interrupts anyhow, I suspect, and it's a thing that have constantly befuddled me. As far as I can tell, there is a problematic race condition in the old signal mechanism, and that is the reason (I believe) why the new semantics were introduced). The problem goes like this: You have two child processes. One exit, and you get into your signal handler. In there you then call wait to reap the child and process things. You then call wait again, and repeat until there are no children left to reap, as you only get one signal, even if you get multiple children that exits. When no more unreaped children exist, you exit the signal handler, and a new signal can be delivered. However, what happens if the second child exists between the call to wait, and the exit from the signal handler? It would seem the signal would get lost, since we are in the process of handling the signal, and a new signal is not delivered during this time. In real hardware this usually don't happen, because the actual interrupt request can be reissued by the device while you are in the interrupt handler. There are some hardware interrupt designs, with edge triggered interrupts, where similar problems can exist, and those you have to be very careful with how you handle them so you don't get to the same kind of race condition. Now, have I misunderstood something about how non-queued signal handling works, or is/was there a problem there? Johnny -- Johnny Billquist || "I'm on a bus || on a psychedelic trip email: b...@softjar.se || Reading murder books pdp is alive! || tryin' to stay hip" - B. Idol
Re: SIGCHLD and sigaction()
> When I install a SIGCHLD handler via sigaction() using SA_SIGINFO, is > it guaranteed that my handler is called (at least) once per > death-of-a-child? "Maybe." It depends on how portable you want to be. Historically, "no": in some older systems, a second SIGCHLD delivered when there's already one pending delivery gets, lost same as any other signal. Then someone - POSIX? SVR4? I don't know - decided to invent a flavour of signal that's more like writes to a pipe: multiple of them can be pending at once. Some systems decided this was sane and implemented it. Personally, I don't like it; I think signals should be much like hardware interrupts in that a second instance happening before the first is serviced gets silently merged. If you want some sort of queued notification of child death, it seems to me a much righter, much more UNIXy, tack to take is to add something like AF_CHILD sockets or some such and get child death notifications that way. (Actually, I'm not sure sockets would work without severe hackery; too much of the socket machinery assumes the data in a socket is independent of who reads from it. Perhaps it would need a new flavour of file descriptor, akin to kevent or timerfd descriptors. I have no doubt it could be done one way or another. But arguably it's best to just use SIGCHLD and a loop that uses WNOHANG.) /~\ The ASCII Mouse \ / Ribbon Campaign X Against HTMLmo...@rodents-montreal.org / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
SIGCHLD and sigaction()
Another question in the context of SIGCHLD: When I install a SIGCHLD handler via sigaction() using SA_SIGINFO, is it guaranteed that my handler is called (at least) once per death-of-a-child? There is sentence in SUS If SA_SIGINFO is set in sa_flags, then subsequent occurrences of sig generated by sigqueue() or as a result of any signal-generating function that supports the specification of an application-defined value (when sig is already pending) shall be queued in FIFO order until delivered or accepted; that may cover this but that I don't understand.