Skip site navigation (1)Skip section navigation (2)
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>