-
Notifications
You must be signed in to change notification settings - Fork 571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vfork should be used for spawning external processes #15355
Comments
From e@80x24.orgThis is a bug report for perl from e@80x24.org, Perl currently uses fork + exec for spawning processes with Despite the implementation of copy-on-write (CoW) under Linux, In my attached example (vfork.perl) using Inline::C, it only takes This is with a 10MB string in memory. Increasing the size of the Keep in mind vfork is tricky to use and some of the caveats The mainline Ruby implementation has been using vfork since Disclaimer: I'm a member of the ruby-core team, but I probably Unfortunately my knowledge of Perl internals is weak at this Thank you for the many years of Perl 5! Flags: Site configuration information for perl 5.24.0: Configured by ew at Mon May 23 22:06:43 UTC 2016. Summary of my perl5 (revision 5 version 24 subversion 0) configuration: @INC for perl 5.24.0: Environment for perl 5.24.0: |
From e@80x24.orgvfork.perl# Copyright 2016 Eric Wong <e@80x24.org>
# licensed under the same terms as Perl itself
# The following example shows the advantage of vfork over fork for
# spawning processes under Linux. Keep in mind vfork is tricky
# and some of the caveats are documented at: https://ewontfix.com/7/
use strict;
use warnings;
use Time::HiRes qw(gettimeofday tv_interval);
use Inline C => <<'VFORK_SPAWN';
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <alloca.h>
#include <signal.h>
#include <assert.h>
#define AV_ALLOCA(av, max) alloca((max = (av_len((av)) + 1)) * sizeof(char *))
static void av2c_copy(char **dst, AV *src, I32 max)
{
I32 i;
for (i = 0; i < max; i++) {
SV **sv = av_fetch(src, i, 0);
dst[i] = sv ? SvPV_nolen(*sv) : 0;
}
dst[max] = 0;
}
static void *deconst(const char *s)
{
union { const char *in; void *out; } u;
u.in = s;
return u.out;
}
/* needs to be safe inside a vfork'ed process */
static void xerr(const char *msg)
{
struct iovec iov[3];
const char *err = strerror(errno); /* should be safe in practice */
iov[0].iov_base = deconst(msg);
iov[0].iov_len = strlen(msg);
iov[1].iov_base = deconst(err);
iov[1].iov_len = strlen(err);
iov[2].iov_base = deconst("\n");
iov[2].iov_len = 1;
writev(2, iov, 3);
_exit(1);
}
#define REDIR(var,fd) do { \
if (var != fd && dup2(var, fd) < 0) \
xerr("error redirecting std"#var ": "); \
} while (0)
/* this does not support arbitrary redirects, yet, just std{in,out,err} */
int vfork_spawn(int in, int out, int err, SV *file, SV *cmdref, SV *envref)
{
AV *cmd = (AV *)SvRV(cmdref);
AV *env = (AV *)SvRV(envref);
const char *filename = SvPV_nolen(file);
pid_t pid;
char **argv, **envp;
I32 max;
sigset_t set, old;
int ret;
argv = AV_ALLOCA(cmd, max);
av2c_copy(argv, cmd, max);
envp = AV_ALLOCA(env, max);
av2c_copy(envp, env, max);
ret = sigfillset(&set);
assert(ret == 0 && "BUG calling sigfillset");
/*
* XXX: not thread-safe, use pthread_sigmask instead of sigprocmask
* if using pthreads
*/
ret = sigprocmask(SIG_SETMASK, &set, &old);
assert(ret == 0 && "BUG calling sigprocmask to block");
pid = vfork();
if (pid == 0) {
int sig;
REDIR(in, 0);
REDIR(out, 1);
REDIR(err, 2);
for (sig = 1; sig < NSIG; sig++)
signal(sig, SIG_DFL); /* ignore errors on signals */
ret = sigprocmask(SIG_SETMASK, &old, NULL);
if (ret != 0)
xerr("sigprocmask failed in vfork child");
execve(filename, argv, envp);
xerr("execve failed");
}
ret = sigprocmask(SIG_SETMASK, &old, NULL);
assert(ret == 0 && "BUG calling sigprocmask to restore");
return (int)pid;
}
VFORK_SPAWN
# The above C code expects env to be an array for execve(2)
my @env = map { "$_=$ENV{$_}" } keys %ENV;
my $nr = 10000; # iterations
# Under the Linux kernel, vfork performance remains stable
# as parent process size grows:
my $mem = 'x' x (1024 * 1024 * 10);
my $t0;
$t0 = [gettimeofday];
foreach (1..$nr) {
my $pid = vfork_spawn(0, 1, 2, '/bin/true', [ 'true' ], \@env);
waitpid($pid, 0);
}
printf "vfork: %0.6f\n", tv_interval($t0, [gettimeofday]);
$t0 = [gettimeofday];
foreach (1..$nr) {
system('/bin/true');
}
printf "system: %0.6f\n", tv_interval($t0, [gettimeofday]);
|
From zefram@fysh.orgvia RT wrote:
This is not a fair comparison. You should compare standard CORE::system() -zefram |
The RT System itself - Status changed from 'new' to 'open' |
From e@80x24.orgZefram via RT <perlbug-followup@perl.org> wrote:
Oops, missed the pipe creation and probably a few other things I tried dumbly swapping out fork() for vfork() in the perl I still haven't had time to digest and learn much about the Anyways, attached is a tiny standalone C program with most $ gcc -o vfork-test -Wall -O2 vfork-test.c $ time ./vfork-test : normal fork() real 0m6.444s $ time ./vfork-test vfork real 0m2.725s That's only with 10M malloc-ed, increasing the malloc-ed size Anyways, I hope the above numbers are convincing enough to have Thank you. |
From e@80x24.org/* gcc -o vfork-test -Wall -O2 vfork-test.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[], char *envp[])
{
int i;
int do_vfork = argc > 1 && !strcmp(argv[1], "vfork");
char * const cmd[] = { "/bin/true", 0 };
size_t n = 1024 * 1024 * 10;
char *mem = malloc(n);
memset(mem, 'a', n); /* make sure it's really allocated */
for (i = 0; i < 10000; i++) {
pid_t pid = do_vfork ? vfork() : fork();
if (pid == 0) {
execve(cmd[0], cmd, envp);
write(2, "exec error\n", 11);
_exit(1);
}
waitpid(pid, 0, 0);
}
return 0;
} |
From @LeontOn Tue, May 24, 2016 at 1:54 AM, via RT <perlbug-followup@perl.org> wrote:
It appears we removed vfork support 15 years ago in 52e18b1. I think the It may actually be sane to implement system on top of posix_spawn though Not sure either option is really worth the development effort though, any Leon |
From vano@mail.mipt.ruConfigure has a question about whether to use vfork(), warning that If you're going down this road, you should start your research by Besides, posix_spawn() is the current POSIX standard that is intended to |
From e@80x24.orgLeon Timmermans <fawaka@gmail.com> wrote:
Sadly, in the past 15 years process sizes have gotten bigger
Good point, I'll give posix_spawn a shot if nobody beats me to Ruby doesn't use posix_spawn since it needs to support
Who knows what I'll be working on :> But yeah, I plan to look at getting the dash shell to support |
From e@80x24.org
Another note: I hit ENOMEM when attempting to spawn one small subprocess from The kernel could not know the fork() was for an execve() shortly Using posix_spawn (where glibc uses CLONE_VFORK behind-the-scenes) |
From Mark@Overmeer.net* Eric Wong (e@80x24.org) [190128 09:23]:
Well, reading fork(2) tells me ENOMEM fork() failed to allocate the necessary kernel structures It's not directly about the normal RAM used by the giant process, but You may be able to find an error or warning in dmesg about which resource Mark Overmeer MSc MARKOV Solutions |
From @eserteDana Mon, 28 Jan 2019 02:55:53 -0800, Mark@Overmeer.net reče:
I doubt it's about kernel memory. Just tried a experiment on a recent Linux system (debian/stretch) with 32GB RAM + 1 GB Swap (at the moment used memory by other processes: < 1GB). The following script just allocates memory and then tries a simple system() call: #!/usr/bin/perl use strict; my $alloc_gb = shift || 4; my $mb = " " x 1024**2; system 'echo', 'system() was successful'; __END__ Running script allocating 16GB of RAM: $ perl /tmp/m.pl 16 No logging happens in any of the files in /var/log at this time. Running the script using with 15GB is successful. Now another script using spawn() provided by POSIX::RT::Spawn (which unfortunately does not build anymore for perl >= 5.28.0): #!/usr/bin/perl use strict; my $alloc_gb = shift || 4; my $mb = " " x 1024**2; my $pid = spawn 'echo', 'spawn() was successful' __END__ No problem running this script with 28GB. Regards, |
I'll update this request.. perl5 should use the posix_spawn interface instead of (vfork|fork)/exec) on
|
Should we assume an |
yes, OR. thanks for poiting this out. |
Migrated from rt.perl.org#128227 (status was 'open')
Searchable as RT128227$
The text was updated successfully, but these errors were encountered: