Date: Sun, 11 Oct 1998 11:09:41 +0800 From: Peter Wemm <peter@netplex.com.au> To: Bruce Evans <bde@FreeBSD.ORG> Cc: cvs-committers@FreeBSD.ORG, cvs-all@FreeBSD.ORG Subject: Re: cvs commit: src/lib/libc/gen popen.c Message-ID: <199810110309.LAA18511@spinner.netplex.com.au> In-Reply-To: Your message of "Sat, 10 Oct 1998 12:30:45 MST." <199810101930.MAA28329@freefall.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Bruce Evans wrote:
> bde 1998/10/10 12:30:45 PDT
>
> Modified files:
> lib/libc/gen popen.c
> Log:
> vfork -> fork. The child calls execl() which calls malloc(), so
> vfork() can't be used. We could use alloca() in execl() so that
> it can be called between vfork() and execve(), but a "portable"
> popen() shouldn't depend on this. Calling execle() instead of
> execl() should be fairly safe, since execle() is supposed to be
> callable from signal handlers and signal handlers can't call
> malloc(). However, execle() is broken.
Why not fix it properly by not using execl()?
Index: popen.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/gen/popen.c,v
retrieving revision 1.10
diff -u -r1.10 popen.c
--- popen.c 1998/10/10 19:30:45 1.10
+++ popen.c 1998/10/11 02:11:46
@@ -62,6 +62,7 @@
struct pid *cur;
FILE *iop;
int pdes[2], pid, twoway;
+ char *argv[4];
/*
* Lite2 introduced two-way popen() pipes using socketpair().
@@ -84,7 +85,12 @@
return (NULL);
}
- switch (pid = fork()) {
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = command;
+ argv[3] = NULL;
+
+ switch (pid = vfork()) {
case -1: /* Error. */
(void)close(pdes[0]);
(void)close(pdes[1]);
@@ -116,7 +122,7 @@
}
(void)close(pdes[1]);
}
- execl(_PATH_BSHELL, "sh", "-c", command, NULL);
+ execv(_PATH_BSHELL, argv);
_exit(127);
/* NOTREACHED */
}
fork() is significantly slower than vfork(), especially with a complex
address space with lots of maps that fork() has to clone. It'll all the
malloc calls in execl() too.
On statically linked binaries, fork+exit takes around 5 times longer than
vfork+exit:
$Id: lat_proc.c,v 1.5 1995/11/08 01:40:21 lm Exp $
Static Process fork+exit: 302 microseconds
Static Process vfork+exit: 63 microseconds
Static Process fork+execve: 1250 microseconds
Static Process vfork+execve: 994 microseconds
Static Process fork+/bin/sh -c: 5977 microseconds
Static Process vfork+/bin/sh -c: 5156 microseconds
While with a dynamically linked binary (and hence a more complicated
address space to clone and set up in copy-on-write mode), fork() takes 13.3
times longer:
$Id: lat_proc.c,v 1.5 1995/11/08 01:40:21 lm Exp $
Process fork+exit: 848 microseconds
Process vfork+exit: 64 microseconds
Process fork+execve: 5712 microseconds
Process vfork+execve: 5090 microseconds
Process fork+/bin/sh -c: 10935 microseconds
Process vfork+/bin/sh -c: 9410 microseconds
Of course, fork() + exec() is the issue here, so fork speed isn't the
whole picture. When combined with exec, it works out that the combination
is between around 10% and 20% slower than the vfork combination.
These numbers are from an ELF-reasonably-current system, on a 686MX-200
system (A pentium-200 class cpu). I don't want to start benchmark wars
[again], but this (modified) lmbench test shows it's worthwhile for
programs that do a lot of popen(). (The mods to lmbench were to make the
test run for 10 times longer for better stability, and an added vfork()
test.) My PPro-200 does much better in UP mode, but worse in SMP mode - no
real suprise there.
Cheers,
-Peter
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe cvs-all" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199810110309.LAA18511>
