[PATCH] dropbear-051: uClinux vfork

Rob Landley rob at landley.net
Thu Oct 23 18:50:30 WST 2008


On Thursday 23 October 2008 03:51:17 Mike Frysinger wrote:
> On Wednesday 22 October 2008, Rob Landley wrote:
> > On Tuesday 21 October 2008 00:01:56 Mike Frysinger wrote:
> > > -	if ((pid = fork()) == -1)
> > > +#ifdef __uClinux__
> > > +	pid = vfork();
> > > +#else
> > > +	pid = fork();
> > > +#endif /* __uClinux__ */
> > > +	if (pid == -1)
> > >  		fatal("do_local_cmd: fork: %s", strerror(errno));
> >
> > If it's ever safe to call vfork() from a given place, then it should
> > always be safe to call it from that place, so the #ifdef isn't really
> > necessary.
> >
> > Although vfork() has more restrictive semantics than fork() does, it's
> > not really less efficient than doing a fork().  Just less flexible.  A
> > system with an mmu can do a vfork() just fine, so if you've cleared that
> > it's ok to use it there, there's no real reason for not to just do it all
> > the time. In fact, being consistent means you have a single codepath and
> > the same behavior on uClinux as everywhere else.
>
> this is certainly all true ... ive just given up on trying to convince
> people as vfork() tends to scare them.  especially if they dont understand
> vfork() semantics and the app starts behaving in ways they dont recognize.

Dropbear's maintained by a smart guy, I think he can cope.

I remember I wrote up some extensive vfork() documentation, but I suspect it 
was when I was working for timesys and they probably put it on their 
proprietary customer-only mailing list so you have a to pay them to see it.  
I could write up another one, I suppose, but I remember getting all the 
details right in the explanation was fiddly.  Here's a quick "sort of wrong 
but you'll understand how to use it" explanation.

You can sort of treat vfork() as a sort of kernel-side setjmp().  After 
vfork(), you still only have one process running, you've just told the kernel 
to checkpoint the old state.  As soon as you do an exec() it'll launch the 
new process and longjmp() the current process back to the vfork() point.  As 
with all uses of setjmp()/longjmp(), if you've changed the stack or the heap 
since then it's still that way after the longjmp(), so you really really 
don't want to do things like return from the function that called vfork() 
before calling exec() to longjmp() you back to the vfork() point.

Telling the process to exit() also does the longjmp() thing (gotta have a way 
to clean up if the exec() fails), but the problem is that exit() also 
does "cleanup" work (it closes stdout, frees memory, and calls atexit(), and 
who knows what else), and doing a longjmp() back into the middle of the 
program after closing down stdio isn't going to end well.  But since it's the 
exit _system_call_ that does the longjmp() back to the vfork() checkpoint 
(remember, vfork() is handled by the kernel, not by libc) then you can use 
the special _exit() call which just callsthe system call without flushing 
stdout or calling atexit() or any of the other "cleanup" things.  (You _only_ 
want to do that to trigger the longjmp() back to the vfork() point, 
ordinarily you _want_ the cleanup exit() does.)

This explanation isn't quite what the kernel's actually doing behind the 
scenes, but it should give you a good mental model of how to use vfork.  
Treat is as a setjmp() that makes the next exec() or _exit() system call act 
like a longjmp() (and makes exec() spawn a new process).

This whole song and dance is simply to avoid having to do "copy on write" 
memory mappings, which fork() relies on and which a nommu system hasn't got 
hardware support for.

Sigh, I should write up some new vfork documentation.  The vfork man page is 
useless...

> -mike

Rob



More information about the Dropbear mailing list