Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 30 Jul 2012 12:24:08 +0200
From:      Jilles Tjoelker <jilles@stack.nl>
To:        freebsd-hackers@freebsd.org
Subject:   system() using vfork() or posix_spawn()
Message-ID:  <20120730102408.GA19983@stack.nl>

next in thread | raw e-mail | index | archive | help
People sometimes use system() from large address spaces where it would
improve performance greatly to use vfork() instead of fork().

A simple approach is to change fork() to vfork(), although I have not
tried this. It seems safe enough to use sigaction and sigprocmask system
calls in the vforked process.

Alternatively, we can have posix_spawn() do the vfork() with signal
changes. This avoids possible whining from compilers and static
analyzers about using vfork() in system.c. However, I do not like the
tricky code for signals and that it adds lines of code.

This is lightly tested.

Index: lib/libc/stdlib/system.c
===================================================================
--- lib/libc/stdlib/system.c	(revision 238371)
+++ lib/libc/stdlib/system.c	(working copy)
@@ -42,16 +42,21 @@
 #include <unistd.h>
 #include <paths.h>
 #include <errno.h>
+#include <spawn.h>
 #include "un-namespace.h"
 #include "libc_private.h"
 
+extern char **environ;
+
 int
 __system(const char *command)
 {
 	pid_t pid, savedpid;
-	int pstat;
+	int error, pstat;
 	struct sigaction ign, intact, quitact;
-	sigset_t newsigblock, oldsigblock;
+	sigset_t newsigblock, oldsigblock, defmask;
+	const char *argv[4];
+	posix_spawnattr_t attr;
 
 	if (!command)		/* just checking... */
 		return(1);
@@ -65,28 +70,36 @@
 	ign.sa_flags = 0;
 	(void)_sigaction(SIGINT, &ign, &intact);
 	(void)_sigaction(SIGQUIT, &ign, &quitact);
+	(void)sigemptyset(&defmask);
+	if ((intact.sa_flags & SA_SIGINFO) != 0 ||
+	    intact.sa_handler != SIG_IGN)
+		(void)sigaddset(&defmask, SIGINT);
+	if ((quitact.sa_flags & SA_SIGINFO) != 0 ||
+	    quitact.sa_handler != SIG_IGN)
+		(void)sigaddset(&defmask, SIGQUIT);
 	(void)sigemptyset(&newsigblock);
 	(void)sigaddset(&newsigblock, SIGCHLD);
 	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
-	switch(pid = fork()) {
-	case -1:			/* error */
-		break;
-	case 0:				/* child */
-		/*
-		 * Restore original signal dispositions and exec the command.
-		 */
-		(void)_sigaction(SIGINT, &intact, NULL);
-		(void)_sigaction(SIGQUIT,  &quitact, NULL);
-		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
-		execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL);
-		_exit(127);
-	default:			/* parent */
+	argv[0] = "sh";
+	argv[1] = "-c";
+	argv[2] = command;
+	argv[3] = NULL;
+	if ((error = posix_spawnattr_init(&attr)) != 0 ||
+	    (error = posix_spawnattr_setsigmask(&attr, &oldsigblock)) != 0 ||
+	    (error = posix_spawnattr_setsigdefault(&attr, &defmask)) != 0 ||
+	    (error = posix_spawnattr_setflags(&attr,
+	    POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK)) != 0 ||
+	    (error = posix_spawn(&pid, _PATH_BSHELL, NULL, &attr,
+	    __DECONST(char **, argv), environ)) != 0) {
+		pid = -1;
+		errno = error;
+	} else {
 		savedpid = pid;
 		do {
 			pid = _wait4(savedpid, &pstat, 0, (struct rusage *)0);
 		} while (pid == -1 && errno == EINTR);
-		break;
 	}
+	posix_spawnattr_destroy(&attr);
 	(void)_sigaction(SIGINT, &intact, NULL);
 	(void)_sigaction(SIGQUIT,  &quitact, NULL);
 	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);

-- 
Jilles Tjoelker



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120730102408.GA19983>