TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Fork() without exec() is dangerous in large programs (2016)

128 pointsby goranmoominalmost 3 years ago

23 comments

garethrowlandsalmost 3 years ago
&quot;The received wisdom suggests that Unix’s unusual combination of fork() and exec() for process creation was an inspired design. In this paper, we argue that fork was a clever hack for machines and programs of the 1970s that has long outlived its usefulness and is now a liability. We catalog the ways in which fork is a terrible abstraction for the modern programmer to use, describe how it compromises OS implementations, and propose alternatives.&quot;<p>from <i>A Fork in the Road</i>, &lt;<a href="https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;uploads&#x2F;prod&#x2F;2019&#x2F;04&#x2F;fork-hotos19.pdf" rel="nofollow">https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;uploads&#x2F;prod&#x2F;2019&#x2F;0...</a>&gt;
评论 #31742900 未加载
评论 #31746993 未加载
评论 #31743280 未加载
评论 #31756431 未加载
jordemortalmost 3 years ago
If you mix fork with threads, you&#x27;re going to have a [undefined behavior] time. It seems like if you link with the sqlite that comes with macOS, you&#x27;re using threads whether you like it or not. I think ending up at &quot;you shouldn&#x27;t use fork() at all&quot; is a bit of an extreme conclusion, though.<p>BTW, article title needs a (2016). It appears that the relevant Python bug has long since been closed, by avoiding linking with the system sqlite on macOS.
评论 #31742490 未加载
评论 #31743238 未加载
评论 #31745109 未加载
infogulchalmost 3 years ago
The dense fog lifts, tree branches part, a ray of light shines down on the ruins of a moss-covered pedestal revealing the hidden intentions of the ancients. A plaque states &quot;The operational semantics of the most basic primitives of your operating system are optimized to simplify the implementation of command line shells.&quot; You look upon the pedestal, pause in respect, then turn away disappointed but unsurprised. As you walk you shake your head trying to evict the after image of a beam of light illuminating a turd.
评论 #31746100 未加载
评论 #31745882 未加载
ridiculous_fishalmost 3 years ago
fish shell uses posix_spawn sometimes because of its performance benefits. We can&#x27;t use it in the following cases:<p>1. No analog to tcsetpgrp, so it&#x27;s no good if job control is enabled<p>2. No analog to fchdir, meaning you have to synchronize with fchdir elsewhere in the progarm<p>3. Error codes do not convey enough information for good error messages (e.g. if a file doesn&#x27;t exist, posix_spawn doesn&#x27;t tell you which file)<p>4. Inconsistent behavior around dup2 fd redirections and CLO_EXEC.<p>5. Inconsistent behavior for shebangless scripts<p>These are basically deal-breakers so fish also supports a fork&#x2F;exec path. However the performance benefits of posix_spawn are too real to ignore so fish uses posix_spawn when it can, and fork&#x2F;exec when it must.
评论 #31746504 未加载
评论 #31743404 未加载
评论 #31743261 未加载
评论 #31743421 未加载
Strilancalmost 3 years ago
Another danger using fork is it duplicates the internal state of pseudo random number generators. It&#x27;s a great way to accidentally take the same random samples in every process, utterly trashing any statistics you were intending to do. Bonus: the python multiprocessing module silently uses fork by default. Person A writes a &quot;make multiprocessing convenient&quot; library, Person B writes a sampling library, you put them together and... <i>whoops!</i>.
评论 #31743726 未加载
评论 #31747840 未加载
zokieralmost 3 years ago
See also this recent 340 (!) comment thread about the issues of fork <a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=30502392" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=30502392</a>
评论 #31744551 未加载
chubotalmost 3 years ago
FWIW this is the same reason you can&#x27;t implement implement a portable Unix shell in portable Go. (And similar issues with an init daemon)<p>Go only exports os.ForkExec() -- there is no os.Fork() or os.Exec(), because the things you can do between the calls could break Go&#x27;s threaded runtime. (Goroutines are implemented with OS threads.)<p>Some elaboration on that: <a href="https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;hj3np3&#x2F;mvdan_sh_posix_shell_go#c_qszuer" rel="nofollow">https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;hj3np3&#x2F;mvdan_sh_posix_shell_go#c_qszuer</a><p>That is, the space between fork and exec is where pipelines are implemented, but also entire subinterpreters&#x2F;subshells. The shell actually uses copy-on-write usefully. (And yes I&#x27;m aware that there&#x27;s a good argument that the shell is almost the ONLY program that needs fork() !)<p>----<p>A lot of people have asked me why not implement Oil in Go and various other languages, so I wrote this page:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;oilshell&#x2F;oil&#x2F;wiki&#x2F;FAQ:-Why-Not-Write-Oil-in-X%3F" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;oilshell&#x2F;oil&#x2F;wiki&#x2F;FAQ:-Why-Not-Write-Oil-...</a><p>So the funny thing is that Python is a lower level language than Go for this particular problem. It doesn&#x27;t do anything weird with regard to syscalls. I&#x27;m still looking for help on this (and donations to pay people other than me):<p><i>Oil Is Being Implemented &quot;Middle Out&quot;</i> <a href="https:&#x2F;&#x2F;www.oilshell.org&#x2F;blog&#x2F;2022&#x2F;03&#x2F;middle-out.html" rel="nofollow">https:&#x2F;&#x2F;www.oilshell.org&#x2F;blog&#x2F;2022&#x2F;03&#x2F;middle-out.html</a>
评论 #31742870 未加载
评论 #31742322 未加载
评论 #31742493 未加载
krylonalmost 3 years ago
The devil, as they say, is in the details.<p>But to be fair, the only times I can recall using fork() without exec() were forking network servers, and that was mostly me learning about doing network stuff, and a forking server was the easiest to implement manually.<p>Oh yeah, and that one time I accidentally wrote a fork bomb trying to stress test a DNS server. At least I learned something from my mistake. ;-)<p>EDIT: To me, using fork() without exec() is kind of like operator overloading - there are cases where it absolutely is the right tool, but these aren&#x27;t very numerous, so one should exercise caution. A lot.
elankartalmost 3 years ago
Let’s also not forget to call our how APIs have to be “fork” aware. I’m surprised fork is still widely in used given all of these downsides
评论 #31741239 未加载
wruzaalmost 3 years ago
Curious why there isn’t an interface in which all required handles and resources could be passed to a child process explicitly. E.g.:<p><pre><code> execvpehm( ..., int *handles, size_t, void **pages, size_t, &#x2F;* etc *&#x2F; ); </code></pre> Would remove so many headaches with concurrency and accidental inheritance.
评论 #31747495 未加载
tuxokoalmost 3 years ago
The suggestions here aren&#x27;t really great. What you should do is already written in the fork(2) manpage <a href="https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;fork.2.html" rel="nofollow">https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man2&#x2F;fork.2.html</a><p>&quot;After a fork() in a multithreaded program, the child can safely call only async-signal-safe functions (see signal-safety(7)) until such time as it calls execve(2).&quot;<p>So just use only async-singal-safe function <a href="https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man7&#x2F;signal-safety.7.html" rel="nofollow">https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man7&#x2F;signal-safety.7.html</a><p>I don&#x27;t know why so many people still hit this issue when it already told you what you can do and not do in the document. I&#x27;ve done this sort of things without any issue.
评论 #31742538 未加载
评论 #31747502 未加载
dangalmost 3 years ago
Discussed at the time:<p><i>Fork() without exec() is dangerous in large programs</i> - <a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=12302539" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=12302539</a> - Aug 2016 (101 comments)
billpgalmost 3 years ago
I asked this a year or so ago. Interesting to read this article in light of that discussion.<p><a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=863871" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=863871</a> (13 years? Yikes!)
评论 #31746377 未加载
Skunkletonalmost 3 years ago
Odd to not see any mention of vfork. vfork solves the problems with fork&#x2F;exec for large programs.
评论 #31741095 未加载
评论 #31743028 未加载
kazinatoralmost 3 years ago
The problems with fork in the face of threads are caused by threads, not by fork. Fork was there first, and it is part of a system that is designed and integrated well.<p>Threads were bolted onto Unix in a hamfisted way, breaking more than just fork. For instance, threads broke relative paths, requiring &quot;at&quot; functions like <i>openat</i> to be invented, an ugly stop-gap measure. Threads were badly integrated with signal handling too, another example.<p>Blaming those existing mechanisms is purely an emotional argument, from the perspective of being infatuated with threads.<p>The design of threads (coming from various efforts that became POSIX threads) came from such an infatuation: the desire to get any kinds of threads working at any cost, while ignoring the global state that exists in a Unix process, and the need to make a lot of it thread-local, or at least optionally so.<p>A thread-local working directory or signal mask would have caused difficulties in hack thread implementations that used user space scheduling or M:N (M user space threads to N kernel tasks).<p>The situation we have today largely comes from the initial reluctance to accept the fact that each thread has to be an entity known to the kernel; the belief that user space threads are viable into the long-term future.
ttoinoualmost 3 years ago
&gt; Only use fork in toy programs. The challenge is that successful toy programs grow into large ones, and large programs eventually use threads. It might be best just to not bother.<p>How do you create a new process and pipe it data in a fast fashion without using fork, exec or posix_spawn ?
评论 #31742515 未加载
评论 #31742149 未加载
评论 #31743744 未加载
londons_explorealmost 3 years ago
One other option is to fork all the threads too.<p>Since you probably don&#x27;t know what all the other threads in your process are up to, your only option is to attach a debugger to all of them, halt them all, and copy all their state into brand new threads in the child process.<p>Do it all correctly and you end up with a multi-threaded-fork.<p>You still need to fix up signal handlers, interrupted syscalls, various notification API&#x27;s that no longer work, memory mapped temp files used for IPC, pipes and sockets, and a bunch of other things.<p>But a fork of a complex process is possible. It just isn&#x27;t easy.
medocalmost 3 years ago
fork() also presents performance issues for programs with a large virtual space. Here vfork() helps, but it has even more pitfalls than fork(). I had written a small doc about converting the recollindex Recoll indexer from fork() to vfork() a while ago: <a href="https:&#x2F;&#x2F;www.lesbonscomptes.com&#x2F;recoll&#x2F;pages&#x2F;idxthreads&#x2F;forkingRecoll.html" rel="nofollow">https:&#x2F;&#x2F;www.lesbonscomptes.com&#x2F;recoll&#x2F;pages&#x2F;idxthreads&#x2F;forki...</a>
layer8almost 3 years ago
New programming language implementations should maybe make fork() and multithreading be mutually exclusive at link time by default, and only allow them together in an unsafe-I-know-what-I’m-doing mode (if at all).
perryizgr8almost 3 years ago
It&#x27;s only dangerous if you use libraries without fully understanding what they&#x27;re doing. And most well designed libraries will avoid creating threads, and will do so only when you make it explicit that you want it to happen.<p>I also find that libraries that absolutely need to make their own threads are better off being their own process. Then you can use proper communication methods to pass data.
评论 #31742684 未加载
olliejalmost 3 years ago
And it’s explicitly unsupported on macOS at least.
legalcorrectionalmost 3 years ago
Won&#x27;t stop the *nix know-nothings from criticizing Windows for not natively supporting fork().
评论 #31742986 未加载
throwaway892238almost 3 years ago
&gt; When I ran into this problem, I was just trying to run all of Bluecore&#x27;s unit tests on my Mac laptop. We use nose&#x27;s multiprocess mode, which uses Python&#x27;s multiprocessing module to utilize multiple CPUs. Unfortunately, the tests hung, even though they passed on our Linux test server.<p>There will never be a time at which you can reliably expect any program developed on one system to &quot;just work&quot; on a different system. This person wasted a lot of time tracking down what was essentially a portability bug. Did they <i>need</i> this to be portable? Was this time well spent generating business value?<p>Pick one system for development through production, stick to it. There will be portability bugs hiding in your code, but you will never have to fix them. You will be upset for a minute that you can&#x27;t use a different system, but you will get over it.
评论 #31742411 未加载