From 3fb5edd5046fcd04f359351f47e70ad37816977a Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Sun, 5 Feb 2012 12:21:25 -0800 Subject: [PATCH] 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. Obtained-from: FreeBSD 230998 --- bin/sh/eval.c | 11 ++++++++++- bin/sh/jobs.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- bin/sh/jobs.h | 3 ++- bin/sh/var.c | 5 ++++- bin/sh/var.h | 4 +++- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/bin/sh/eval.c b/bin/sh/eval.c index b1d250b812..9e429ffc95 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)eval.c 8.9 (Berkeley) 6/8/95 - * $FreeBSD: src/bin/sh/eval.c,v 1.119 2012/01/22 14:00:33 jilles Exp $ + * $FreeBSD: src/bin/sh/eval.c,v 1.120 2012/02/04 23:12:14 jilles Exp $ */ #include @@ -924,6 +924,15 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) 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) { diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c index 4ee7b8af0b..33dd86a366 100644 --- a/bin/sh/jobs.c +++ b/bin/sh/jobs.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)jobs.c 8.5 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/jobs.c,v 1.95 2011/06/13 21:03:27 jilles Exp $ + * $FreeBSD: src/bin/sh/jobs.c,v 1.97 2012/02/04 23:12:14 jilles Exp $ */ #include @@ -56,6 +56,7 @@ #undef CEOF /* syntax.h redefines this */ #endif #include "redir.h" +#include "exec.h" #include "show.h" #include "main.h" #include "parser.h" @@ -883,6 +884,54 @@ forkshell(struct job *jp, union node *n, int mode) } +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. diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h index bb6b629e5c..72274f3dd5 100644 --- a/bin/sh/jobs.h +++ b/bin/sh/jobs.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)jobs.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/jobs.h,v 1.21 2011/06/13 21:03:27 jilles Exp $ + * $FreeBSD: src/bin/sh/jobs.h,v 1.22 2012/02/04 23:12:14 jilles Exp $ */ /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ @@ -96,6 +96,7 @@ void showjobs(int, int); pid_t getjobpgrp(char *); 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); diff --git a/bin/sh/var.c b/bin/sh/var.c index a3a5a9b86e..7d54848ea8 100644 --- a/bin/sh/var.c +++ b/bin/sh/var.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)var.c 8.3 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/var.c,v 1.63 2011/06/17 10:21:24 jilles Exp $ + * $FreeBSD: src/bin/sh/var.c,v 1.64 2012/02/04 23:12:14 jilles Exp $ */ #include @@ -93,6 +93,7 @@ struct var vps2; struct var vps4; struct var vvers; static struct var voptind; +struct var vdisvfork; int forcelocal; @@ -124,6 +125,8 @@ static const struct varinit varinit[] = { #endif { &voptind, 0, "OPTIND=1", getoptsreset }, + { &vdisvfork, VUNSET, "SH_DISABLE_VFORK=", + NULL }, { NULL, 0, NULL, NULL } }; diff --git a/bin/sh/var.h b/bin/sh/var.h index 44890e305e..fbbba59927 100644 --- a/bin/sh/var.h +++ b/bin/sh/var.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)var.h 8.2 (Berkeley) 5/4/95 - * $FreeBSD: src/bin/sh/var.h,v 1.24 2011/06/13 21:03:27 jilles Exp $ + * $FreeBSD: src/bin/sh/var.h,v 1.25 2012/02/04 23:12:14 jilles Exp $ */ /* @@ -83,6 +83,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; @@ -113,6 +114,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); -- 2.41.0