Eduardo Tongson wrote:
AS> Ah, so DragonFly is doggedly sticking to M:N eh? Interesting. AS>
ET> DragonFly will be using a MxN variant, M:Ncpus
How does M:Ncpus differ exactly from M:N ?
AS> The Linux clone() call can take different parameters to make AS> spawned code take on either the characteristics of threads AS> OR processes. Not sure if there is a tradeoff involved or AS> if it is truly the more elegant solution.
ET> clone() purpose was to create and allow _threads_ to run just ET> like processes (they are actually processes in the kernel) which ET> are allowed to block in the kernel without intervening with ET> each other. The problem with 1:1 is the overhead of forking a ET> kernel process for each thread and expensive context switches and ET> operations to have them behave accordingly to spec. M:N mitigates ET> the disadvantages of the M:1 and 1:1 models.
Note that linux processes are different from Unix processes in that they are much more efficient (NT processes have the most overhead, but then NT supported threads before most Unices did and NT /threads/ are much more efficient than Unix /processes/).
According to the very interesting article at http://www.itworld.com/nl/lnx_tip/02092001/ ,
1) "Unlike processes, threads run within the same address space and share their process' data. In such environments, the thread creation and destruction takes place considerably faster compared to a full-blown process' creation or destruction. Under Solaris, for example, launching a new thread is about 70 times faster than launching a new process. Linux, however, is radically different."
2) "Under Linux, threads and processes are almost indistinguishable except for one thing: threads run within the same address space whereas processes have distinct address spaces. However, no differences exist between the two from a scheduler point-of-view. Thus, a context switch between two threads of the same process essentially jumps from one code location to another, plus setting a few CPU registers."
3) "In this regard, Linux newcomers often are unaware of the substantial differences between Linux and other operating systems. To implement concurrency, they use multithreading exclusively, mistakenly assuming as high an overhead associated with Linux multiprocessing as on other platforms. However, this is not the case. In fact, many multithreaded applications ported from other platforms to Linux can benefit from replacing multithreading with multiprocessing; this will eliminate the overhead of critical sections and other locking mechanisms used in multithreaded applications."
-------------------------------------------------------------------
As 2) hints at, one of the things the clone() parameters allow you to control is address space sharing. If the clone() parameters specify shared address space, it's essentially creating a thread. If you allocate your own address space (with copy-on-write) for the newly spawned 'execution entity', it would be a process.
What 3) is saying is that you can throw away all the nasty critical sections, mutexes, semaphores, (i.e. locking mechanism kludges) if you use processes - clone() with parameters specifying own address space - rather than threads - clone() with parameters specifying /shared/ address space.
These thread locking mechanisms create their own kind of performance overhead but more importantly, tremendous debugging headaches. In fact the latter has led to a sentiment that 'threads are evil'.
So if there is little overhead difference between spawning Linux 'processes' and 'threads', spawning a 'process' (i.e. clone()ing a newly spawned 'execution entity' with its own copy-on-write memory/address space) is far more desirable.
Besides thread/process creation time, there is the issue of context switching. Now, process context switching will admittedly introduce more overhead than thread context switching, but we are also led to believe that under Linux, the penalty is much smaller(?) than on traditional Unix.
-------------------------------------------------------------
So I've been wondering from way back: if NPTL threads are so efficiently spawned and scheduled, can't the same apply to 2.6 fork()ed processes?
If you use clone(), you are not portable, but why can't fork() be nearly as efficient as NPTL threads (or at least exhibit similar dramatic improvement compared to the old fork() ) since they are both based on the same underlying clone() call??
What special feature of 2.6 does NPTL take advantage of? futexes? Anything else besides that? And can't we apply something similar to fork()ed processes?
fork(), in many ways, offers a far cleaner API than pthreads, so if we can get an efficient fork() on top of 2.6's clone(), we can shun the overengineered pthreads API (and threads in general) and get greatly improved performance for both new and old fork()-based unix code (the latter, probably without even needing to recompile).
Such code would not be very efficient on other unixes, but would still compile properly... and assuming the above scenario holds, would fly on Linux without all the nastiness of thread programming.
Also, if neither fork() nor pthreads will do it for you, you can always invent your own concurrency mechanisms. If you want to take advantage of kernel scheduling (necessary for SMP, but not otherwise) though, they will have to built on top of clone() on Linux.
AS> (Side note on thread APIs: Linus and the other hackers on the AS> kernel mailing list hate the posix threads API and consider it AS> overengineered. The main reason to use it is only because it AS> is a cross-*nix standard. You can probably do a direct clone() AS> call in your app if you don't care about running on under *nixes.)
ET> This is a bad thing as it implies that users of Linux should conform ET> to _their_ preferred methods rather than using portable standards. ET> As always this will limit dev and user freedom. Good thing they ET> allowed Ulrich Drepper et al to provide the portable standard in ET> this case NPTL which delivers quite well.
Agree. Anyway, I forgot that the clone() system call can be wrapped up by traditional fork() and *fork() calls (to get portability) while I was writing the above. :-D
-- reply-to: a n d y @ n e o t i t a n s . c o m http://www.neotitans.com Web and Software Development
-- Philippine Linux Users' Group (PLUG) Mailing List [email protected] (#PLUG @ irc.free.net.ph) Official Website: http://plug.linux.org.ph Searchable Archives: http://marc.free.net.ph . To leave, go to http://lists.q-linux.com/mailman/listinfo/plug . Are you a Linux newbie? To join the newbie list, go to http://lists.q-linux.com/mailman/listinfo/ph-linux-newbie
