forkfd: add FFD_VFORK_SEMANTICS flag

This flag asks forkfd() to use vfork semantics wherever available. That
is, suspend the calling process execution until the child either does an
execve(2) or _exit(2). The advantage of that is that it puts lower
pressure on the OS VMM system, as the number of pages that need to be
copy-on-write duplicated is much smaller (still not zero, as at least
the stack in the child will be written to).

However, the only implementation that supports using this flag for now
is Linux's pidfd. It would be possible to add to FreeBSD, but pdfork(2)
does not have a flag for this behavior -- if it gets one, we can add
support for it later. Everywhere else, we need to force the child to not
exit until we store the child process's PID in the ProcessInfo structure
we allocated, which means the parent process must run before we even
return from forkfd().

Change-Id: I1bee3bc466a04f19bd6efffd15f447f28c201aa9
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Thiago Macieira 2020-02-17 11:41:16 -08:00 committed by Edward Welbourne
parent ba5e2ce49a
commit defd49f7bf
3 changed files with 15 additions and 5 deletions

View File

@ -611,12 +611,18 @@ static int create_pipe(int filedes[], int flags)
* descriptor. You probably want to set this flag, since forkfd() does not work
* if the original parent process dies.
*
* @li @C FFD_USE_FORK Tell forkfd() to actually call fork() instead of a
* @li @c FFD_USE_FORK Tell forkfd() to actually call fork() instead of a
* different system implementation that may be available. On systems where a
* different implementation is available, its behavior may differ from that of
* fork(), such as not calling the functions registered with pthread_atfork().
* If that's necessary, pass this flag.
*
* @li @c FFD_VFORK_SEMANTICS Tell forkfd() to use semantics similar to
* vfork(), if that's available. For example, on Linux with pidfd support
* available, this will add the CLONE_VFORK option. On most other systems,
* including Linux without pidfd support, this option does nothing, as using
* the actual vfork() system call would cause a race condition.
*
* The file descriptor returned by forkfd() supports the following operations:
*
* @li read(2) When the child process exits, then the buffer supplied to

View File

@ -38,9 +38,10 @@
extern "C" {
#endif
#define FFD_CLOEXEC 1
#define FFD_NONBLOCK 2
#define FFD_USE_FORK 4
#define FFD_CLOEXEC 1
#define FFD_NONBLOCK 2
#define FFD_USE_FORK 4
#define FFD_VFORK_SEMANTICS 8
#define FFD_CHILD_PROCESS (-2)

View File

@ -147,7 +147,10 @@ int system_forkfd(int flags, pid_t *ppid, int *system)
}
*system = 1;
pid = sys_clone(CLONE_PIDFD, &pidfd);
unsigned long cloneflags = CLONE_PIDFD;
if (flags & FFD_VFORK_SEMANTICS)
cloneflags |= CLONE_VFORK;
pid = sys_clone(cloneflags, &pidfd);
if (ppid)
*ppid = pid;