Skip to content

Commit

Permalink
sh: Use vfork in a few common cases.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Peter Avalos committed Feb 5, 2012
1 parent 723ecb5 commit 3fb5edd
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 5 deletions.
11 changes: 10 additions & 1 deletion bin/sh/eval.c
Expand Up @@ -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 <sys/time.h>
Expand Down Expand Up @@ -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) {
Expand Down
51 changes: 50 additions & 1 deletion bin/sh/jobs.c
Expand Up @@ -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 <sys/ioctl.h>
Expand All @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion bin/sh/jobs.h
Expand Up @@ -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. */
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion bin/sh/var.c
Expand Up @@ -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 <unistd.h>
Expand Down Expand Up @@ -93,6 +93,7 @@ struct var vps2;
struct var vps4;
struct var vvers;
static struct var voptind;
struct var vdisvfork;

int forcelocal;

Expand Down Expand Up @@ -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 }
};
Expand Down
4 changes: 3 additions & 1 deletion bin/sh/var.h
Expand Up @@ -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 $
*/

/*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 3fb5edd

Please sign in to comment.