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.
This commit is contained in:
parent
d5d7f81a2c
commit
caf29fab8c
@ -921,6 +921,15 @@ evalcommand(union node *cmd, int flags, 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) {
|
||||
|
@ -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, 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.
|
||||
|
@ -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);
|
||||
|
@ -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 }
|
||||
};
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user