Differences between Multi-threading Models - multithreading

Many-to-One Model
One-to-One Model
Many-to-Many Model
Advantages and disadvantages of each model ?
Can you give an example ?
EDIT:
One thing is confusing me with the Many-to-One Model
I'm quoting the book:
"Thread management is done by the thread library in user space, so it
is efficient; but the entire process will block if a thread makes a
blocking system call. Also, because only one thread can access the
kernel at a time, multiple threads are unable to run in parallel on
multiprocessors"
Does it mean all processes in kernel will be blocked, due to the fact that the swapping is done by the application, not by OS scheduler.
(since in this model we manage threads in user-mode) ?
Or, only the threads belonging to the same process of the thread that made the blocking system call will be blocked ?
Thanks in advance!

We have to assign user level threads to kernel level threads, based on the this the mapping can be:
One to one (One user thread mapped to one kernel level thread)
Many to one(Many User level threads mapped to one kernel level thread)
Many to many(Many User level threads mapped to many kernel level threads)
Here the number of kernel level threads are generally set to lesser than number of user level thread, since management of kernel level threads is much more expensive since it involves kernel intervention in their(kernel level thread's) management.
Because of this reason only the fourth mapping of "one to many"(one user level thread to multiple kernel level threads) to one does not make sense.
"Thread management is done by the thread library in user space, so it is efficient; but the entire process will block if a thread makes a blocking system call. Also, because only one thread can access the kernel at a time, multiple threads are unable to run in parallel on multiprocessors"
this example may help to understand this line:
Does it mean all processes in kernel will be blocked, due to the fact that the swapping is done by the application, not by OS scheduler. (since in this model we manage threads in user-mode) ? Or, only the threads belonging to the same process of the thread that made the blocking system call will be blocked ?
One process's threads are independent of other process's threads.So only the threads belonging to the same process of the thread that made the blocking system call will be blocked.
I hope this does make sense to you...

I can see your problem. You have a horrible book.
You're asking about a couple of related issues. First of all, there are two general ways to implement threads.
1) Threads are implemented in a library using timers. In systems that schedule processes for execution this is the only way to do thread. This was ONLY way to do threads in the olde days. This system is usually called "user threads." User threads are multiplexed within a process. The process does the scheduling of its own threads.
The mythical advantage of "user threads" over "kernel threads" (below) is that they are more efficient. This is what your quoted passage is referring to. The statement "the entire process will block if a thread makes a blocking system call" is only true on some [unix] systems.
2) Threads are implemented in the operating system. A process consists of an address space and one or more threads. The operating system kernel schedules THREADS for execution rather than PROCESSES. These are kernel thread.
Note that even if the system supports kernel threads, it is possible for a process to use user threads. The two are not mutually exclusive. However, a system that does not natively support kernel threads can only use user thread.
That's the simple way to explain the different threading models.
-=-=-=-=-=-=-=-=-=-=-=-
The one-to-one, many-to-one, and many-to-many models are a needless confusion for students. Now we have to get into overlapping terminology.
Let's change the terminology around. For #1, instead of calling the schedulable unit of execution a "process" we call it a "kernel thread." There can only be one kernel thread per process in this model. Then the threads in the process are are "user threads." Any number of user threads execute within/are mapped to a kernel thread. This is then the many-to-one model. User threads = many-to-one.
If we have the operating system create the thread (a kernel thread), let's theoretically call what is being executed a "user thread." Each user thread maps to/executed in one and only one kernel thread. This is then the one-to-one model.
The many-to-one model is the same as what is normally called "user threading model."
The terminology is starting to get nonsensical because there is only one thread but we are calling it a user thread mapped to a kernel thread.
The one-to-one model is what is normally called the kernel threading model.
Lastly, we get to the many-to-many model. It is theoretical BS. In theory, there could be many user threads mapped to many kernel threads. In other words, a single user thread could execute within different kernel threads. I have never heard of system implementing threads this way and I cannot imagine any practicable advantage of such a system.
-=-=-=-=-=-=-=-=-=-=-=-=-
As to your last question, in some operating systems, blocking system calls block also block the timers used to implement user threads (a/k/a many-to-one). If one thread make blocking call, it blocks all the other threads in the PROCESS from executing until the blocking call completes.
This blocking does not occur in all systems (something an OS textbook should point out).

Related

If you have One-to-One-Mapping of Kernel- and User-Threads, why isn't process be blocked, if it makes a system-call?

I often read that when you have many-to-one-mapping, a sys-call would block the whole process, but a one-to-one would'nt. But why? The Thread that makes the sys-call is blocked anyway and can't make a switch-command to the other user-thread.
The kernel schedules the threads not the processes globally.
In a very general case when a process is made of just its main thread, we use a kind of shortcut in the manner of speaking and say that the kernel schedules those processes, but it is just a (very common) corner case.
Each thread can have its own priority, its own affinity to some CPUs...
But by default these properties are inherited from the main thread in the process it belongs to, and if we don't change them explicitly, we can have the illusion that all these threads inside a single process form one single entity from the scheduler point of view; but it is not the case.
When one thread is blocked on a system call for example, this does not prevent the other threads from being run.
The blocked thread does not decide of anything concerning the next thread to be run (except if we explicitly build a dedicated applicative synchronisation mechanism with cond-vars...).
The switch to another thread, in the same or another process, is entirely decided by the OS scheduler.
Another confusing situation for example is when a segmentation fault occurs in a thread.
Since the address space is shared between all the threads inside a single process, this is the process as a whole that must be terminated.
Then all its threads disappear at once, which gives again the illusion that all these threads form one single entity from the scheduler point of view; but it is more related to address space management than scheduling.
note: there may exist some user-space implementations of threads in which the OS scheduler have no way to consider these threads as distinct (it sees only the main thread of the process).
Depending on the internal details of such user-space implementations, a blocking system call could lead to blocking the entire process or not.
Nowadays, it is much more common to rely on the native threads provided by the kernel.
I think there's one very specific thing that you're missing.
When you have a one-to-one mapping of user threads to kernel threads, that means that each user thread has its own kernel thread. There's no "switch-command to the other user thread" because the other user thread is another kernel thread, and it's the kernel's job to switch between kernel threads. On modern operating systems, the kernel can easily switch kernel threads when a thread is blocked in a system call.
When you have a many-to-one mapping of user threads, that means a single kernel thread is expected to run code for more than one user thread. Here, the user code has to do something to cause that same kernel thread to execute code for another user thread. It can't do that if it's blocked in a system call.

one-to-one multi-threading model

In silberschatz "Operating System Concepts" book, section 4.3.2 says that
one-to-one model provides more concurrency than the many-to-one model
by allowing another thread to run when a thread makes a blocking
system call. It also allows multiple threads to run in parallel on
multiprocessors.
I have two questions here:
How can one thread be blocked and other mapped on kernel thread?
Dont we know that if one thread is blocked, entire process of that
user-level thread is blocked?
The OS considers user-level threads
as one thread only. It cant be assigned to multiple
processors/cores. Isn't the below given line contradicting that
idea?
It also allows multiple threads to run in parallel on
multiprocessors
Your understanding of user level threads and kernel level threads is not correct, in particular you need to understand how user level threads are mapped to kernel level threads. So first lets define some terms
Kernel thread
A thread (schedulable task) that is created and managed by the kernel. Every kernel level thread is represented by some data structure which contains information related to the thread. In the case of Linux it is task_struct. Kernel threads are the only threads that are considered by the CPU scheduler for scheduling.
Note : Kernel thread is a bit of misnomer as Linux kernel doesn't distinguish between a thread and a process, schedulable task would better describe this entity.
User thread
A thread that is created and managed by some library such as JVM above kernel level. The library that creates these threads is responsible for their management that is which thread runs and when.
User level to kernel level mapping
Now you can create as many user level threads as you want but to execute them you need to create some kernel level thread (task_struct). This creation of kernel level threads can be done in many ways
One to one model
In this case whenever you create a user level thread your library asks the kernel to create a new kernel level thread. In the case of Linux your library will use clone system call to create a kernel level thread.
Many to one model
In this case your library creates only one kernel level thread (task_struct). No matter how many user level threads you create they all share the same kernel level thread, much like the processes running on a single core CPU. The point to understand is that your library here acts much like the CPU scheduler, it schedules many user level threads on single kernel level thread.
Now to your question
The OS considers user-level threads as one thread only. It can’t be
assigned to multiple processors/cores. Isn't the below given line
contradicting that idea?
If you were using many to one model, in that case you will have only one kernel level thread for all of your user level threads and hence they cannot run on different CPU’s.
But if you are using a one to one model then each of your user level threads has a corresponding kernel level thread that can be scheduled individually and hence user level threads can run on different CPU’s given that you have more than one CPU.
You are suffering from a confusing book.
There are real threads (aka kernel threads, 1 to 1 model) and there are simulated threads (aka user threads, many to 1 model).
Some books make this more confusing by throwing a hypothetical many to many model.
User threads are obsolete. Any operating system book worth reading these days would treat them that way and describe them in historical terms.
How can one thread be blocked and other mapped on kernel thread? Dont we know that if one thread is blocked, entire process of that user-level thread is blocked?
You either have user threads or kernel thread. An application that did both would be royally screwed up.
The OS considers user-level threads as one thread only. It cant be assigned to multiple processors/cores. Isn't the below given line contradicting that idea?
In ye olde days a process was considered to be an execution stream and an address space. There were no threads. When threads became necessary (largely due to the need for Ada support), they were simulated using timers. The behavior of threads varied by operating system.
In Eunuchs variants, blocking calls block the process entirely. Thus in simulated (user) threads a blocking call in one thread would block all threads. This is not true on all operating systems.
Now, a process is one or more execution streams and an address space. That is what you ought be learning; not a bunch of technobabble.
A book that talks about threads in terms of 1-to-1 or many-to-1 models is only fit to line cat boxes.

Mapping of user level and kernel level thread

While going through OPERATING SYSTEM PRINCIPLES, 7TH ED
(By Abraham Silberschatz, Peter Baer Galvin, Greg Gagne), i encountered a
statement in Thread Scheduling Section.It is given as -:
To run on a CPU, user-level threads must ultimately be mapped
to an associated kernel-level thread, although this mapping may
be indirect and may use a lightweight process (LWP).
The first half of the statement i.e
To run on a CPU, user-level threads must ultimately be mapped to an associated kernel-level
is trying to say that When a user level thread is executed ,it will need support from kernel thread like system calls.
But i am completely stuck in other half i.e
although this mapping may
be indirect and may use a lightweight process (LWP)
What does it really mean ???
Please help me out !
You're reading a book that is notoriously crapola. Threads are implemented in two ways.
In the olde days (and still persists on some operating systems) there were just processes. A process consisted of an execution stream and an address space.
When languages that needed thread support (e.g., Ada—"tasks") there was a need to create libraries to implement threads. The libraries used timers to switch among the various threads within the process. This is poor man's threading. The major drawback here is that, even when you have multiple processors, all the threads of a process run on the same processor. The threads are just interleaved execution within a single process that executes on one processors.
These are sometimes called "user level threads." Some books call this the "many-to-one model."
To say
To run on a CPU, user-level threads must ultimately be mapped to an associated kernel-level thread
is highly misleading. There [usually] ARE no kernel threads in this model; just processes. Multiple threads run interleaved in a process. To call this a mapping "to an associated kernel-level thread" is misleading and overly theoretical.
This is mumbo jumbo.
although this mapping may be indirect and may use a lightweight process (LWP)
The next stage in operating system evolution here was for the operating system to support threads directly. Instead of a process being an execution stream + address-space, a process became one-or-more-threads + address-space. Instead of scheduling processes for execution, the OS schedules threads for execution.
Those are kernel threads.
Your book is making the simple complex.
These days the term Light Weight Processes and threads are used interchangeably.
although this mapping may be indirect and may use a lightweight
process (LWP)
I know the above statement is confusing(Notice the 2 mays). I can think only 1 thing which the above statement signifies is that:
Earlier when linux supported only user-level threads, the kernel was unaware of the fact that there are multiple user-level threads, and the way it handled these multiple threads was by associating all of them to a light weight process(which kernel sees as a single scheduling and execution unit) at kernel level.
So associating a kernel-level thread with each user-level thread is kind of direct mapping and associating a single light weight process with each user-level thread is indirect mapping.

Multithreading Models - One to Many model

I've been reading the dinosaur book and have been confused by this particular model.
The books says that for the one to many model "Thread management is done by the thread library in user space, so it is efficient; but the entire process will block if a thread makes a blocking system call. Also, because only one thread can access the kernel at a time, multiple threads are unable to run in parallel on multiprocessors"
What I'm confused about is what is meant by an entire process will block if a blocking system call is made? Does this mean if I have a multi-threaded program and one of it's threads blocks then all of its threads will have to wait, effectively stalling the program?
If a program undergoing execution causes a block with this model does it mean that another separate program can't be swapped in to be executed because the kernel thread is blocking? If that answer is YES another program(process) could be swapped in than why couldn't a multi-threaded program simply execute another one of its threads while the blocking thread is forced to wait?
If you manage your threads in user level, it means that the swapping is done by your application, not by OS scheduler. Each thread must reach some point where he surrenders (or loses) the control to the management mechanism, but that mechanism is also user-level, so if one of the threads is in the middle of doing a system call - your thread management system (and through that all the other threads) must wait until the kernel code is done.
The OS is still active all the time, and may still preempt the entire program, so other processes will not starve, only the internal "threads" you manage yourself. These threads can't get started during that block because the mechanism responsible of starting them is also blocked by the kernel.

Difference between user-level and kernel-supported threads?

I've been looking through a few notes based on this topic, and although I have an understanding of threads in general, I'm not really to sure about the differences between user-level and kernel-level threads.
I know that processes are basically made up of multiple threads or a single thread, but are these thread of the two prior mentioned types?
From what I understand, kernel-supported threads have access to the kernel for system calls and other uses not available to user-level threads.
So, are user-level threads simply threads created by the programmer when then utilise kernel-supported threads to perform operations that couldn't be normally performed due to its state?
Edit: The question was a little confusing, so I'm answering it two different ways.
OS-level threads vs Green Threads
For clarity, I usually say "OS-level threads" or "native threads" instead of "Kernel-level threads" (which I confused with "kernel threads" in my original answer below.) OS-level threads are created and managed by the OS. Most languages have support for them. (C, recent Java, etc) They are extremely hard to use because you are 100% responsible for preventing problems. In some languages, even the native data structures (such as Hashes or Dictionaries) will break without extra locking code.
The opposite of an OS-thread is a green thread that is managed by your language. These threads are given various names depending on the language (coroutines in C, goroutines in Go, fibers in Ruby, etc). These threads only exist inside your language and not in your OS. Because the language chooses context switches (i.e. at the end of a statement), it prevents TONS of subtle race conditions (such as seeing a partially-copied structure, or needing to lock most data structures). The programmer sees "blocking" calls (i.e. data = file.read() ), but the language translates it into async calls to the OS. The language then allows other green threads to run while waiting for the result.
Green threads are much simpler for the programmer, but their performance varies: If you have a LOT of threads, green threads can be better for both CPU and RAM. On the other hand, most green thread languages can't take advantage of multiple cores. (You can't even buy a single-core computer or phone anymore!). And a bad library can halt the entire language by doing a blocking OS call.
The best of both worlds is to have one OS thread per CPU, and many green threads that are magically moved around onto OS threads. Languages like Go and Erlang can do this.
system calls and other uses not available to user-level threads
This is only half true. Yes, you can easily cause problems if you call the OS yourself (i.e. do something that's blocking.) But the language usually has replacements, so you don't even notice. These replacements do call the kernel, just slightly differently than you think.
Kernel threads vs User Threads
Edit: This is my original answer, but it is about User space threads vs Kernel-only threads, which (in hindsight) probably wasn't the question.
User threads and Kernel threads are exactly the same. (You can see by looking in /proc/ and see that the kernel threads are there too.)
A User thread is one that executes user-space code. But it can call into kernel space at any time. It's still considered a "User" thread, even though it's executing kernel code at elevated security levels.
A Kernel thread is one that only runs kernel code and isn't associated with a user-space process. These are like "UNIX daemons", except they are kernel-only daemons. So you could say that the kernel is a multi-threaded program. For example, there is a kernel thread for swap. This forces all swap issues to get "serialized" into a single stream.
If a user thread needs something, it will call into the kernel, which marks that thread as sleeping. Later, the swap thread finds the data, so it marks the user thread as runnable. Later still, the "user thread" returns from the kernel back to userland as if nothing happened.
In fact, all threads start off in kernel space, because the clone() operation happens in kernel space. (And there's lots of kernel accounting to do before you can 'return' to a new process in user space.)
Before we go into comparison, let us first understand what a thread is. Threads are lightweight processes within the domain of independent processes. They are required because processes are heavy, consume a lot of resources and more importantly,
two separate processes cannot share a memory space.
Let's say you open a text editor. It's an independent process executing in the memory with a separate addressable location. You'll need many resources within this process, such as insert graphics, spell-checks etc. It's not feasible to create separate processes for each of these functionalities and maintain them independently in memory. To avoid this,
multiple threads can be created within a single process, which can
share a common memory space, existing independently within a process.
Now, coming back to your questions, one at a time.
I'm not really to sure about the differences between user-level and kernel-level threads.
Threads are broadly classified as user level threads and kernel level threads based on their domain of execution. There are also cases when one or many user thread maps to one or many kernel threads.
- User Level Threads
User level threads are mostly at the application level where an application creates these threads to sustain its execution in the main memory. Unless required, these thread work in isolation with kernel threads.
These are easier to create since they do not have to refer many registers and context switching is much faster than a kernel level thread.
User level thread, mostly can cause changes at the application level and the kernel level thread continues to execute at its own pace.
- Kernel Level Threads
These threads are mostly independent of the ongoing processes and are executed by the operating system.
These threads are required by the Operating System for tasks like memory management, process management etc.
Since these threads maintain, execute and report the processes required by the operating system; kernel level threads are more expensive to create and manage and context switching of these threads are slow.
Most of the kernel level threads can not be preempted by the user level threads.
MS DOS written for Intel 8088 didn't have dual mode of operation. Thus, a user level process had the ability to corrupt the entire operating system.
- User Level Threads mapped over Kernel Threads
This is perhaps the most interesting part. Many user level threads map over to kernel level thread, which in-turn communicate with the kernel.
Some of the prominent mappings are:
One to One
When one user level thread maps to only one kernel thread.
advantages: each user thread maps to one kernel thread. Even if one of the user thread issues a blocking system call, the other processes remain unaffected.
disadvantages: every user thread requires one kernel thread to interact and kernel threads are expensive to create and manage.
Many to One
When many user threads map to one kernel thread.
advantages: multiple kernel threads are not required since similar user threads can be mapped to one kernel thread.
disadvantage: even if one of the user thread issues a blocking system call, all the other user threads mapped to that kernel thread are blocked.
Also, a good level of concurrency cannot be achieved since the kernel will process only one kernel thread at a time.
Many to Many
When many user threads map to equal or lesser number of kernel threads. The programmer decides how many user threads will map to how many kernel threads. Some of the user threads might map to just one kernel thread.
advantages: a great level of concurrency is achieved. Programmer can decide some potentially dangerous threads which might issue a blocking system call and place them with the one-to-one mapping.
disadvantage: the number of kernel threads, if not decided cautiously can slow down the system.
The other part of your question:
kernel-supported threads have access to the kernel for system calls
and other uses not available to user-level threads.
So, are user-level threads simply threads created by the programmer
when then utilise kernel-supported threads to perform operations that
couldn't be normally performed due to its state?
Partially correct. Almost all the kernel thread have access to system calls and other critical interrupts since kernel threads are responsible for executing the processes of the OS. User thread will not have access to some of these critical features. e.g. a text editor can never shoot a thread which has the ability to change the physical address of the process. But if needed, a user thread can map to kernel thread and issue some of the system calls which it couldn't do as an independent entity. The kernel thread would then map this system call to the kernel and would execute actions, if deemed fit.
Quote from here :
Kernel-Level Threads
To make concurrency cheaper, the execution aspect of process is separated out into threads. As such, the OS now manages threads and processes. All thread operations are implemented in the kernel and the OS schedules all threads in the system. OS managed threads are called kernel-level threads or light weight processes.
NT: Threads
Solaris: Lightweight processes(LWP).
In this method, the kernel knows about and manages the threads. No runtime system is needed in this case. Instead of thread table in each process, the kernel has a thread table that keeps track of all threads in the system. In addition, the kernel also maintains the traditional process table to keep track of processes. Operating Systems kernel provides system call to create and manage threads.
Advantages:
Because kernel has full knowledge of all threads, Scheduler may decide to give more time to a process having large number of threads than process having small number of threads.
Kernel-level threads are especially good for applications that frequently block.
Disadvantages:
The kernel-level threads are slow and inefficient. For instance, threads operations are hundreds of times slower than that of user-level threads.
Since kernel must manage and schedule threads as well as processes. It require a full thread control block (TCB) for each thread to maintain information about threads. As a result there is significant overhead and increased in kernel complexity.
User-Level Threads
Kernel-Level threads make concurrency much cheaper than process because, much less state to allocate and initialize. However, for fine-grained concurrency, kernel-level threads still suffer from too much overhead. Thread operations still require system calls. Ideally, we require thread operations to be as fast as a procedure call. Kernel-Level threads have to be general to support the needs of all programmers, languages, runtimes, etc. For such fine grained concurrency we need still "cheaper" threads.
To make threads cheap and fast, they need to be implemented at user level. User-Level threads are managed entirely by the run-time system (user-level library).The kernel knows nothing about user-level threads and manages them as if they were single-threaded processes.User-Level threads are small and fast, each thread is represented by a PC,register,stack, and small thread control block. Creating a new thread, switiching between threads, and synchronizing threads are done via procedure call. i.e no kernel involvement. User-Level threads are hundred times faster than Kernel-Level threads.
Advantages:
The most obvious advantage of this technique is that a user-level threads package can be implemented on an Operating System that does not support threads.
User-level threads does not require modification to operating systems.
Simple Representation: Each thread is represented simply by a PC, registers, stack and a small control block, all stored in the user process address space.
Simple Management: This simply means that creating a thread, switching between threads and synchronization between threads can all be done without intervention of the kernel.
Fast and Efficient: Thread switching is not much more expensive than a procedure call.
Disadvantages:
User-Level threads are not a perfect solution as with everything else, they are a trade off. Since, User-Level threads are invisible to the OS they are not well integrated with the OS. As a result, Os can make poor decisions like scheduling a process with idle threads, blocking a process whose thread initiated an I/O even though the process has other threads that can run and unscheduling a process with a thread holding a lock. Solving this requires communication between between kernel and user-level thread manager.
There is a lack of coordination between threads and operating system kernel. Therefore, process as whole gets one time slice irrespect of whether process has one thread or 1000 threads within. It is up to each thread to relinquish control to other threads.
User-level threads requires non-blocking systems call i.e., a multithreaded kernel. Otherwise, entire process will blocked in the kernel, even if there are runable threads left in the processes. For example, if one thread causes a page fault, the process blocks.
User Threads
The library provides support for thread creation, scheduling and management with no support from the kernel.
The kernel unaware of user-level threads creation and scheduling are done in user space without kernel intervention.
User-level threads are generally fast to create and manage they have drawbacks however.
If the kernel is single-threaded, then any user-level thread performing a blocking system call will cause the entire process to block, even if other threads are available to run within the application.
User-thread libraries include POSIX Pthreads, Mach C-threads,
and Solaris 2 UI-threads.
Kernel threads
The kernel performs thread creation, scheduling, and management in kernel space.
kernel threads are generally slower to create and manage than are user threads.
the kernel is managing the threads, if a thread performs a blocking system call.
A multiprocessor environment, the kernel can schedule threads on different processors.
5.including Windows NT, Windows 2000, Solaris 2, BeOS, and Tru64 UNIX (formerlyDigital UN1X)-support kernel threads.
Some development environments or languages will add there own threads like feature, that is written to take advantage of some knowledge of the environment, for example a GUI environment could implement some thread functionality which switch between user threads on each event loop.
A game library could have some thread like behaviour for characters. Sometimes the user thread like behaviour can be implemented in a different way, for example I work with cocoa a lot, and it has a timer mechanism which executes your code every x number of seconds, use fraction of a seconds and it like a thread. Ruby has a yield feature which is like cooperative threads. The advantage of user threads is they can switch at more predictable times. With kernel thread every time a thread starts up again, it needs to load any data it was working on, this can take time, with user threads you can switch when you have finished working on some data, so it doesn't need to be reloaded.
I haven't come across user threads that look the same as kernel threads, only thread like mechanisms like the timer, though I have read about them in older text books so I wonder if they were something that was more popular in the past but with the rise of true multithreaded OS's (modern Windows and Mac OS X) and more powerful hardware I wonder if they have gone out of favour.

Resources