On Wed, Jul 26, 2017 at 06:27:43PM +0200, Joerg Schilling wrote:
> Robert Elz <k...@munnari.oz.au> wrote:

> >     Date:        Tue, 25 Jul 2017 19:06:13 +0200
> >     From:        Joerg Schilling <joerg.schill...@fokus.fraunhofer.de>
> >     Message-ID:  
> > <59777a85.lYd+zInvutTipp/s%joerg.schill...@fokus.fraunhofer.de>

> >   | ash does not use vfork() at all.

> > And life is a continuous process of learning new things - I had no idea.

> > But now I look back through time, I see that vfork() did not appear in
> > the NetBSD shell until May 2000 (and not in FreeBSD until 2012).

> Interesting to see that other also added vfork() support to their shells.

> >From my metering, I know that on Solaris vfork() is 3x faster than fork()
> even tough the fork() implementation in SunOS uses CopyOnWrite since 1988.

> As a result, I typically see aprox. 30% better performance with a "configure" 
> run when vfork() is used.

That difference is a bit higher than I saw in "real" benchmarks on
FreeBSD, but the differences I saw were still significant, both on PC
and embedded hardware. I think using vfork() somehow is definitely
interesting for a scripting shell.

> > Amazing.   In that case, my hypothesis as to why there is the split
> > between redirect word expansion, and the following open/close is clearly
> > nonsense - the code that does that (as far as NetBSD is concerned anyway,
> > I have never seen an original ash shell) was in version 1.1 (March 1993),
> > and is roughly

> >     expredir();
> >     forkshell();
> >     if (child) {
> >             redirect();
> >             exec();
> >     }

> > (highly simplified and with lots removed, of course).

I think this was deliberate so that side effects from ${foo?} and
${foo=bar} affect the outer shell environment instead of just the single
command.

> You would need:
> 
>       expredir();
>       forkshell();
>       if (child) {
>               redirect();
>               exec();
>       } else {
>               repair_mods_from_the_child();
>       }

> if you use vfork.

This looks icky because opening a redirection may block (for example if
the file is a fifo), which leads to the vforked child sticking around in
its strange state for longer than expected.

If this approach is used for background simple commands, it is
definitely broken because the redirection opens should execute in
parallel with the rest of the script. Things like
  mkfifo f; something >f & exec 3<f
would deadlock.

One way to avoid these problems is to perform redirections in the parent
process like ksh does; a background command will fork before that point.

Alternatively, as in FreeBSD sh, only use vfork() for simple cases and
keep the old fork() code path for the rest (such as if there is a
redirection or a variable assignment in the simple command or if the
shell is interactive). This gets most of the performance benefit but
minimizes the risk of bugs from using vfork() incorrectly and ensures
vfork() does not affect the semantics seen by the script.

-- 
Jilles Tjoelker

Reply via email to