NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/49017: vfork does not suspend all threads
The following reply was made to PR kern/49017; it has been noted by GNATS.
From: Robert Elz <kre%munnari.OZ.AU@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc:
Subject: Re: kern/49017: vfork does not suspend all threads
Date: Fri, 07 Apr 2017 10:07:46 +0700
Date: Thu, 6 Apr 2017 15:05:01 +0000 (UTC)
From: Nico Williams <Nico.Williams%twosigma.com@localhost>
Message-ID: <20170406150501.356437A2B8%mollari.NetBSD.org@localhost>
| I'll take it much further: it is fork() that is EVIL, and vfork()
| that is GOOD.
|
| Here's my rationale for such an extraordinary statement:
| https://gist.github.com/nicowilliams/a8a07b0fc75df05f684c23c18d7db234
All that shows is that fork() is (or can be) expensive, which is hardly
news, nothing at all about evilness, in fact, the closest I can see that
it comes are these sentences ...
Since back then programs and processes were small that inelegance was
easy to overlook. But now processes tend to be huge, and that makes
copying even just a parent's resident set, and page table fiddling for
the rest, extremely expensive.
That's kind of like saying that Ferrari's are evil, because they cost
too much if all you do is drive them grocery shopping once a week...
Being expensive (even too expensive to routinely use) is not evil, just
not the right (or perhaps even, just not the best) choice in many cases.
If anything is "evil" from your text (IMO) it would be "But now processes
tend to be huge" - that is the problem, not fork().
But fork() permits
if (fork() > 0)
_exit(0);
which most of the other methods (I know nothing of clone(), so that one
possibly excepted) do not - expensive perhaps (in some cases) but useful
nevertheless.
| Briefly: fork()'s copying and/or COW are terrible and would never have been
| necessary had Dennis Ritchie et. al. thought of vfork()'s semantics.
While I suspect that fork() is really Ken's, not Dennis's (irrelevant here)
I kind of doubt that. First because neither of them is/was in any way
deficient in their thinking (simply ignoring a possibility like that is
not something I would expect) and second, because fork(), expensive or not,
is simply far more general than vfork().
| Besides, fork() has a ton of safety issues (which I mostly
| did not address in that gist,
Nor anywhere else I have seen - I'm sure it is possible to write code
badly enough that fork() would cause problems, (and it is certainly
possible to make a mess using buffered I/O) but almost all of that is
trivially overcome.
| Now, vfork() is... clumsy because of the stack sharing silliness, but it
| predates threads, so its authors probably did not realize that taking a
| callback function and argument to run in a new stack would have been a
| superior design
When vfork() was designed, the total (guaranteed) address space was just
64KB (text, data, stack, all combined). Duplicating stacks (adding an
extra stack - and if you want to be able to return in the child, it actually
means copying the existing stack, while adjusting any self-referencing pointers
that occur there) would have been laughed away as absurd.
In another message Nico.Williams%twosigma.com@localhost (kind of) quotes me:
| Robert Elz <kre%munnari.OZ.AU@localhost> wrote:
| > [ description of vfork_into_fork() elided ]
and then says...
| That's a neat idea, but I don't think it's needed. I can't think of why I
| would ever need it or any time that I could have used it.
Maybe you never would have, but I know of one immediate use - that is /bin/sh
Our sh uses vfork() whenever it can (for the obvious reason) but sometimes,
while evaluating the code to be executed in the sub-shell, it discovers that
it simply cannot do that after a vfork() and really needs a whole new process.
What happens now is that the child sets a magic "do me again using fork()"
flag (in the parent's address space, which it shares of course) and then
exits. The parent observes the flag, fork()'s, and the child starts all over
again. If the child could have simply converted its vfork() state into a
fork() state that wacky dance would not be needed.
Now, of course, the shell could avoid this by examining the tree of commands
to be executed before the fork()/vfork() (it does that for the very common
cases that will certainly require fork() rather than vfork()) but that would
mean duplicating the whole process, initially just to discover which kind of
fork() is required, and then again to actually do the work - for every
sub-shell invocation (more or less every command executed that isn't a
function) and all this for a relatively rare circumstance.
| What I really want is
| pid_t avfork(int (*)(void *), void *);
| which is like vfork() but allocates a new stack, calls the given callback
| in it just like pthread_create() would, and does not stop any threads in
| the parent, not even the one that called it.
I have no objection to that, go ahead, write the code for it, and submit
it, it sounds useful enough to consider at least.
But...
| Note that avfork() would have much the same constraints for the child as
| vfork() does, except, naturally, that the avfork() child could return while
| the vfork() child cannot.
Return to what? You're having it execute a callback, are you saying that
that function can return? Return to where exactly? And what does that
mean? What would be the difference between
child = avfork(func, &sp);
and
if ((child = avfork(&sp)) == 0) func();
??
If there's none, why the need for the callback? If avfork() cannot
actually return in the child, so the second is not possible, then neither
can func() right?
kre
Home |
Main Index |
Thread Index |
Old Index