Bug 14770 – std.process should use lightweight forks where available

Status
NEW
Severity
normal
Priority
P3
Component
phobos
Product
D
Version
D2
Platform
x86_64
OS
All
Creation time
2015-07-04T03:21:56Z
Last change time
2024-12-01T16:24:47Z
Assigned to
No Owner
Creator
rsw0x
Moved to GitHub: phobos#9663 →

Comments

Comment #0 by rsw0x — 2015-07-04T03:21:56Z
fork(even with CoW pages) can be extremely slow with modern memory usages due to the antiquated default page size of OSes because it requires copying the page table. For example, a program using 400MiB of memory with 4KiB pages requires copying 102,400 page structs. On Linux, a page struct in the kernel is at least 72 bytes so this requires a copy of 7.2MiB. This scales linearly with heap size, after some personal testing on Linux I found forking with almost nothing allocated to take ~60 microseconds, 100 MiB ~3 milliseconds, and 1GiB to take 30-45 milliseconds. vfork took a constant 20 microseconds no matter the heap size. Solution: where available, use posix_spawn, vfork, etc. These do not require copying page tables. std.process does not require the page tables since it immediately replaces itself with another process. I marked this all OSes, but really it's all POSIX OSes I guess.
Comment #1 by schveiguy — 2015-07-06T12:46:36Z
CC'ing Lars, this is a good idea.
Comment #2 by dsp — 2015-07-07T05:52:14Z
https://github.com/D-Programming-Language/phobos/pull/3476 I opted for using vfork. It seems we are in all cases doing the right thing, not tempering with the process space and correctly calling execv* or _exit. It seems that the "correct" method would be using posix_spawn, however we need support for this in druntime, so at the moment, I think going with vfork is less invasive while providing the proposed enhancements.
Comment #3 by post — 2015-07-31T07:21:56Z
I think the "correct" thing to do here (on Linux, at least) is to use the clone() function, as described in this article: http://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ NOTE: If anyone wants to take a stab at implementing this for Phobos, don't look at the actual source code for the "popen-noshell" library, as it is LGPL-licensed. Both clone() and vfork() are GNU library wrappers around Linux' clone system call, but clone() allows a much finer control over which data gets copied into the new process. Unlike vfork(), however, clone() is not a drop-in replacement for fork(). It takes a function to execute and a stack space in which to execute it, as opposed to continuing execution from the same point in the child process. In other words, there's a bit more work needed to incorporate it in spawnProcess(). Note that vfork() seems to be generally frowned upon; see for example the NOTES section of the man page: http://man7.org/linux/man-pages/man2/vfork.2.html#NOTES
Comment #4 by kubo39 — 2018-07-30T15:01:33Z
tl;dr: I think spawnProcess uses vfork internally iff Linux available. * We must care about the process privilege, since if the parent process or child process can change effective UID/GID, different privileged processes shares memory. * We must use _exit(2), same as before. * signal handlers may rewrite global variables, so set SIG_DFL before vfork. * Solaris has bug. * Other platform planed(e.g, NetBSD has nice posix_spawn systemcall), but not implement now. Okay, I try to do this. ## References. * posix_spawn(3) requires glibc 2.24+. * posix_spawn(IEEE Std 1003.1-2008) cannot specifies working directory(chdir). * http://www.tedunangst.com/flak/post/OpenBSD-and-vfork OpenBSD's vfork doesn't share memory. * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=9ff72da471a509a8c19791efe469f47fa6977410 glibc's posix_spawn uses clone(2) instead of vfork(2). * https://bugs.ruby-lang.org/issues/11265#note-8 In Solaris, vfork causes dynamic linker problem. * https://github.com/golang/go/commit/9e6b79a5dfb2f6fe4301ced956419a0da83bd025 Go uses clone(CLONE_VFORK | CLONE_VM,..) Linux only. * https://github.com/rust-lang/rust/pull/48624 Rust uses posix_spawn(3) on FreeBSD/macOS/Linux.
Comment #5 by schveiguy — 2018-07-30T15:50:42Z
FYI, new PR by Hiroki Nada: https://github.com/dlang/phobos/pull/6644
Comment #6 by dfj1esp02 — 2018-08-01T13:50:03Z
Maybe have a branch for linux and call clone there, call posix_spawn or fork for other systems. vfork looks scary and posix deprecated it.
Comment #7 by kubo39 — 2018-08-02T15:54:49Z
(In reply to anonymous4 from comment #6) > Maybe have a branch for linux and call clone there, call posix_spawn or fork > for other systems. vfork looks scary and posix deprecated it. If we use clone(2), the stack of the child process is newly allocated and the stack of the parent process is not shared. It's preferred. Maybe I should add clone(2) to druntime before... https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=9ff72da471a509a8c19791efe469f47fa6977410
Comment #8 by robert.schadek — 2024-12-01T16:24:47Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/phobos/issues/9663 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB