4 ms·
> How to use fork safely: 1. Only use fork to immediately call exec. 2. Fork a worker at the beginning of your program, before there can be other threads. 3. On
by zzzcpan 10y ago
> How to use fork safely: 1. Only use fork to immediately call exec. 2. Fork a worker at the beginning of your program, before there can be other threads. 3. Only use fork in toy programs.
4. Stop writing broken multithreaded code altogether or if you must at least run an event loop per core/thread and use a wrapper for fork() to put the system into a fork()able state before forking. It's nice and reliable.
Multithreading by itself is just not a high-level concept to be used reliably by programs.
- evanj 10y agoYou are completely correct: it is in fact possible to use fork if you can carefully control the state of threads in your program. My point is that can be difficult in large software projects, particularly when random APIs like "get the system proxy settings" use threads without you having any ability to control them.
- valarauca1 10y agoFork/Exec is only good if you are going to run a different program then you are currently executing. If you want a series of worker threads that are effectively the same as the master/initial thread. The proper way to do this clone(2) not fork, or fork/exec. When used properly clone allows your group of threads to share a single PID, and TGID (Thread Group IDentifier). This cuts down on kernel resources your process(es) are using. They can natively share file descriptors and memory between each other (improving cache coherence). Also their signals are handled globally for all threads at once, not each thread managing it's own signals like Fork/Exec will result in.
- stsp 10y ago$ man 2 clone man: No entry for clone in the manual.
- qwertyuiop924 10y ago...And why would you want that? The advantage for fork(2) over clone is that you CAN'T share those datastructures, making it harder to write code resulting a deadlock: you have to share explicitely. As for the kernel resources for the process, you'd be surprised how large RAM has gotten these days...
- valarauca1 10y ago...And why would you want that? All your applications share the same virtual memory space. So you have a much higher hit rate in your TLB, then without. It greatly lowers the chance that shared memory will leave L3 cache. Cache hit rates are very important... The advantage for fork(2) over clone is that you CAN'T share those datastructures, making it harder to write code resulting a deadlock Dead locking has nothing to do with how RAM is accessed or how virtual memory is partitioned, it has to do with how you are managing your locking. Modern memory/instruction re-ordering is stupidly fast. If you are locking memory/data structure locations you are likely doing something wrong. Concurrent Memory fences are around 3 orders of magnitude faster then locks. As for the kernel resources for the process, you'd be surprised how large RAM has gotten these days... TLB/L2/L3 cache is still premium real-estate. RAM size is awesome! I love waiting 100,000cycles to get the page I requested.
- qwertyuiop924 10y agoCache hit rates aren't as important as you seem to think. If you're not google, and your app doesn't have hard/soft realtime constraints (videogames, flight control systems, etc.) your app will probably be fast enough. You'd be surprised how fast computers are nowadays... >If you are locking memory/data structure locations you are likely doing something wrong. Oh, concurrent memory fences! Well, that solves all my problems. Let's see. All I have to do is give up all hope of my code ever being portable to mutiple architectures, and than dig around in asm to add support for them to my environment, which can range from mildly annoying (C), to near-impossible (JVM). Or I can use fork(2), and do what programmers have been doing for decades: trading performance for simplicity, and productivity. That'll be a really hard decision.
- valarauca1 10y agoAll I have to do is give up all hope of my code ever being portable to multiple architectures You know nothing about modern compiler atomics. Post 2011 compilers (LLVM, GCC, MSVC, ICC) standardized generic memory fences for C++ and C. These are fully portable as the compiler itself determines how the layout of Acquire/Releases needs to be changed on platform you are compiling too. Atomics are supported on ARM, x64, MIPS64, SPARC64, POWER8, POWER9... So they're very portable. The compiler even manages removing unneeded fences. Like if you add an acquire fence after a CAS load. The CAS load is an acquire fence on x64, but not on POWER8.