Hello Christian, On 9/25/19 3:53 PM, Christian Brauner wrote: > On Wed, Sep 25, 2019 at 03:46:26PM +0200, Michael Kerrisk (man-pages) wrote: >> On 9/24/19 11:53 PM, Christian Brauner wrote: >>> On Tue, Sep 24, 2019 at 11:00:03PM +0200, Michael Kerrisk (man-pages) wrote: >>>> Hello Christian, >>>> >>>>>>> If you're the parent of the process you can do this without CLONE_PIDFD: >>>>>>> pid = fork(); >>>>>>> pidfd = pidfd_open(); >>>>>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0); >>>>>>> if (ret < 0 && errno == ESRCH) >>>>>>> /* pidfd refers to another, recycled process */ >>>>>> >>>>>> Although there is still the race between the fork() and the >>>>>> pidfd_open(), right? >>>>> >>>>> Actually no and my code is even too complex. >>>>> If you are the parent, and this is really a sequence that obeys the >>>>> ordering pidfd_open() before waiting: >>>>> >>>>> pid = fork(); >>>>> if (pid == 0) >>>>> exit(EXIT_SUCCESS); >>>>> pidfd = pidfd_open(pid, 0); >>>>> waitid(pid, ...); >>>>> >>>>> Then you are guaranteed that pidfd will refer to pid. No recycling can >>>>> happen since the process has not been waited upon yet (That is, >>>> >>>> D'oh! Yes, of course. >>>> >>>>> excluding special cases such as where you have a mainloop where a >>>>> callback reacts to a SIGCHLD event and waits on the child behind your >>>>> back and your next callback in the mainloop calls pidfd_open() while the >>>>> pid has been recycled etc.). >>>>> A race could only appear in sequences where waiting happens before >>>>> pidfd_open(): >>>>> >>>>> pid = fork(); >>>>> if (pid == 0) >>>>> exit(EXIT_SUCCESS); >>>>> waitid(pid, ...); >>>>> pidfd = pidfd_open(pid, 0); >>>>> >>>>> which honestly simply doesn't make any sense. So if you're the parent >>>>> and you combine fork() + pidfd_open() correctly things should be fine >>>>> without even having to verify via pidfd_send_signal() (I missed that in >>>>> my first mail.). >>>> >>>> Thanks for the additional detail. >>> >>> You're very welcome. >>> >>>> >>>> I added the following to the pidfd_open() page, to >>>> prevent people making the same thinko as me: >>>> >>>> The following code sequence can be used to obtain a file descrip‐ >>>> tor for the child of fork(2): >>>> >>>> pid = fork(); >>>> if (pid > 0) { /* If parent */ >>>> pidfd = pidfd_open(pid, 0); >>>> ... >>>> } >>>> >>>> Even if the child process has already terminated by the time of >>>> the pidfd_open() call, the returned file descriptor is guaranteed >>>> to refer to the child because the parent has not yet waited on the >>>> child (and therefore, the child's ID has not been recycled). >>> >>> Thanks! I'm fine with the example. The code illustrates the basics. If >>> you want to go overboard, you can mention my callback example and put my >>> SIG_IGN code snippet from my earlier mails (cf. [1] and [2]) in there. >>> But imho, that'll complicate the manpage and I'm not sure it's worth it. >> >> I agree that we should not complicate this discussion with more code, >> but how about we refine the text as follows: >> >> The following code sequence can be used to obtain a file descrip‐ >> tor for the child of fork(2): >> >> pid = fork(); >> if (pid > 0) { /* If parent */ >> pidfd = pidfd_open(pid, 0); >> ... >> } >> >> Even if the child has already terminated by the time of the >> pidfd_open() call, its PID will not have been recycled and the >> returned file descriptor will refer to the resulting zombie >> process. Note, however, that this is guaranteed only if the fol‐ >> lowing conditions hold true: >> >> * the disposition of SIGCHLD has not been explicitly set to >> SIG_IGN (see sigaction(2)); and > > Ugh, I forgot a third one. There's also SA_NOCLDWAIT. When set and > the SIGCHLD handler is set to SIG_DFL then no zombie processes are > created and no SIGCHLD signal is sent. When an explicit handler for > SIGCHLD is set then a SIGCHLD signal is generated but the process will > still not be turned into a zombie... Oh, yes. I added: * the SA_NOCLDSTOP flag was not specified while establishing a handler for SIGCHLD or while setting the disposition of that signal to SIG_DFL (see sigaction(2)); >> * the zombie process was not reaped elsewhere in the program >> (e.g., either by an asynchronously executed signal handler or >> by wait(2) or similar in another thread). >> >> If these conditions don't hold true, then the child process should > > "If any of these conditions does not hold, the child process..." > > That might be clearer. But I leave the call on that to you. :) Yep, your wording is better. Fixed. Thanks, Michael -- Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/