diff options
author | jilles <jilles@FreeBSD.org> | 2012-02-04 23:12:14 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2012-02-04 23:12:14 +0000 |
commit | c9a60ad55a7cdcf983430853a4bdb15d31340f0b (patch) | |
tree | cdf2aa0b0a8655054be3d8275658129bb9b75c99 /bin/sh/jobs.c | |
parent | a3ada8a47cbcc2eecf9773251384c95d920c9cab (diff) | |
download | FreeBSD-src-c9a60ad55a7cdcf983430853a4bdb15d31340f0b.zip FreeBSD-src-c9a60ad55a7cdcf983430853a4bdb15d31340f0b.tar.gz |
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.
Diffstat (limited to 'bin/sh/jobs.c')
-rw-r--r-- | bin/sh/jobs.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c index 232be8b..335d2ca 100644 --- a/bin/sh/jobs.c +++ b/bin/sh/jobs.c @@ -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. |