Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 4 Feb 2012 23:12:15 +0000 (UTC)
From:      Jilles Tjoelker <jilles@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r230998 - head/bin/sh
Message-ID:  <201202042312.q14NCFPQ055994@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Sat Feb  4 23:12:14 2012
New Revision: 230998
URL: http://svn.freebsd.org/changeset/base/230998

Log:
  sh: Use vfork in a few common cases.
  
  This uses vfork() for simple commands and command substitutions containing a
  single simple command, invoking an external program under certain conditions
  (no redirections or variable assignments, non-interactive shell, no job
  control). These restrictions limit the amount of code executed in a vforked
  child.
  
  There is a large speedup (for example 35%) in microbenchmarks. The
  difference in buildkernel is smaller (for example 0.5%) but still
  statistically significant. See
  http://lists.freebsd.org/pipermail/freebsd-hackers/2012-January/037581.html
  for some numbers.
  
  The use of vfork() can be disabled by setting a variable named
  SH_DISABLE_VFORK.

Modified:
  head/bin/sh/eval.c
  head/bin/sh/jobs.c
  head/bin/sh/jobs.h
  head/bin/sh/var.c
  head/bin/sh/var.h

Modified: head/bin/sh/eval.c
==============================================================================
--- head/bin/sh/eval.c	Sat Feb  4 23:00:27 2012	(r230997)
+++ head/bin/sh/eval.c	Sat Feb  4 23:12:14 2012	(r230998)
@@ -921,6 +921,15 @@ evalcommand(union node *cmd, int flags, 
 			if (pipe(pip) < 0)
 				error("Pipe call failed: %s", strerror(errno));
 		}
+		if (cmdentry.cmdtype == CMDNORMAL &&
+		    cmd->ncmd.redirect == NULL &&
+		    varlist.list == NULL &&
+		    (mode == FORK_FG || mode == FORK_NOJOB) &&
+		    !disvforkset() && !iflag && !mflag) {
+			vforkexecshell(jp, argv, environment(), path,
+			    cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL);
+			goto parent;
+		}
 		if (forkshell(jp, cmd, mode) != 0)
 			goto parent;	/* at end of routine */
 		if (flags & EV_BACKCMD) {

Modified: head/bin/sh/jobs.c
==============================================================================
--- head/bin/sh/jobs.c	Sat Feb  4 23:00:27 2012	(r230997)
+++ head/bin/sh/jobs.c	Sat Feb  4 23:12:14 2012	(r230998)
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
 #undef CEOF			/* syntax.h redefines this */
 #endif
 #include "redir.h"
+#include "exec.h"
 #include "show.h"
 #include "main.h"
 #include "parser.h"
@@ -885,6 +886,54 @@ forkshell(struct job *jp, union node *n,
 }
 
 
+pid_t
+vforkexecshell(struct job *jp, char **argv, char **envp, const char *path, int idx, int pip[2])
+{
+	pid_t pid;
+	struct jmploc jmploc;
+	struct jmploc *savehandler;
+
+	TRACE(("vforkexecshell(%%%td, %p, %d) called\n", jp - jobtab, (void *)n,
+	    mode));
+	INTOFF;
+	flushall();
+	savehandler = handler;
+	pid = vfork();
+	if (pid == -1) {
+		TRACE(("Vfork failed, errno=%d\n", errno));
+		INTON;
+		error("Cannot fork: %s", strerror(errno));
+	}
+	if (pid == 0) {
+		TRACE(("Child shell %d\n", (int)getpid()));
+		if (setjmp(jmploc.loc))
+			_exit(exception == EXEXEC ? exerrno : 2);
+		if (pip != NULL) {
+			close(pip[0]);
+			if (pip[1] != 1) {
+				dup2(pip[1], 1);
+				close(pip[1]);
+			}
+		}
+		handler = &jmploc;
+		shellexec(argv, envp, path, idx);
+	}
+	handler = savehandler;
+	if (jp) {
+		struct procstat *ps = &jp->ps[jp->nprocs++];
+		ps->pid = pid;
+		ps->status = -1;
+		ps->cmd = nullstr;
+		jp->foreground = 1;
+#if JOBS
+		setcurjob(jp);
+#endif
+	}
+	INTON;
+	TRACE(("In parent shell:  child = %d\n", (int)pid));
+	return pid;
+}
+
 
 /*
  * Wait for job to finish.

Modified: head/bin/sh/jobs.h
==============================================================================
--- head/bin/sh/jobs.h	Sat Feb  4 23:00:27 2012	(r230997)
+++ head/bin/sh/jobs.h	Sat Feb  4 23:12:14 2012	(r230998)
@@ -91,6 +91,7 @@ void setjobctl(int);
 void showjobs(int, int);
 struct job *makejob(union node *, int);
 pid_t forkshell(struct job *, union node *, int);
+pid_t vforkexecshell(struct job *, char **, char **, const char *, int, int []);
 int waitforjob(struct job *, int *);
 int stoppedjobs(void);
 int backgndpidset(void);

Modified: head/bin/sh/var.c
==============================================================================
--- head/bin/sh/var.c	Sat Feb  4 23:00:27 2012	(r230997)
+++ head/bin/sh/var.c	Sat Feb  4 23:12:14 2012	(r230998)
@@ -94,6 +94,7 @@ struct var vps2;
 struct var vps4;
 struct var vvers;
 static struct var voptind;
+struct var vdisvfork;
 
 int forcelocal;
 
@@ -125,6 +126,8 @@ static const struct varinit varinit[] = 
 #endif
 	{ &voptind,	0,				"OPTIND=1",
 	  getoptsreset },
+	{ &vdisvfork,	VUNSET,				"SH_DISABLE_VFORK=",
+	  NULL },
 	{ NULL,	0,				NULL,
 	  NULL }
 };

Modified: head/bin/sh/var.h
==============================================================================
--- head/bin/sh/var.h	Sat Feb  4 23:00:27 2012	(r230997)
+++ head/bin/sh/var.h	Sat Feb  4 23:12:14 2012	(r230998)
@@ -79,6 +79,7 @@ extern struct var vppid;
 extern struct var vps1;
 extern struct var vps2;
 extern struct var vps4;
+extern struct var vdisvfork;
 #ifndef NO_HISTORY
 extern struct var vhistsize;
 extern struct var vterm;
@@ -109,6 +110,7 @@ extern int initial_localeisutf8;
 #endif
 
 #define mpathset()	((vmpath.flags & VUNSET) == 0)
+#define disvforkset()	((vdisvfork.flags & VUNSET) == 0)
 
 void initvar(void);
 void setvar(const char *, const char *, int);



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