Multithreading model in Linux and Windows - linux

I have been following a course on Operating Systems over the past few months. However, I'd like some clarification on one point that I read. From my understanding, there are three types of multithreading models to map user level threads to kernel level threads -
Many to one model
Many to many model
One to one model
I can see why the many to one model is not very efficient when it comes to parallel processing - because a blocking system call would mean halting of any processing.
However, in the book I'm referring, Operating System Concepts (By Abraham Silberschatz, Greg Gagne and Peter Galvin), it says that both Linux and the Windows family use the one to one model, even though there is the extra overhead of creating a kernel thread for every user thread that's created.
Wouldn't a many to many model be better? Because you have a number of kernel threads, sufficient to have a high degree of parallelism, and you can always opt for a two level model to bind a user level thread to a kernel level thread.
TLDR: Why is the one to one multithreading model preferred over many to many model despite the overhead in Windows and Linux systems?

Wouldn't a many to many model be better?
I'd suggest getting another book. AFAIK the Many-to-Many model is entirely theoretical (If someone knows of a system that uses it, please indicate in a comment). These models are a very poor way of explaining threads.
In ye olde days operating system had no concept of threads. They scheduled processes for execution. In fact, this is still the case for many operating systems.
The need for threads was largely driven by the Ada programming language that required "task" support. In order to have a compliant Ada implementation there had to be a library that simulated threads within a single process. In such a system a process schedules its own thread execution ("user threads). This has the drawback that the threads always run interleaved (never in parallel on different processors).
This is being called "many to one" but that is a poor description of what is happening. Your model calls that "many user threads" are being mapped to a single "kernel thread." In reality, there are no kernel threads. Instead threads are being implemented in the context of a process.
It has become common for operating systems to view processes as an address space with multiple schedulable threads of execution. in such a system the thread is the basic unit of scheduling; not the process. Is is the one-to-one model in your scheme.
it says that both Linux and the Windows family use the one to one model, even though there is the extra overhead of creating a kernel thread for every user thread that's created.
This is kind of BS.There is overhead for a thread no matter how they are implemented. You will often find people claiming that "Many-to-One" is more efficient than "One-to-One." This claim appears to be just urban legend.
Why is the one to one multithreading model preferred over many to many model despite the overhead in Windows and Linux systems?
The "one-to-one" (aka kernel threads) model is preferable because it takes advantage of multiple processors and allows actual execution in parallel. It also avoids blocking problems that can occur in some systems (e.g. Eunuchs).

AFAIK, the "many to many" model implies doing the scheduling in user space, so it means that each program must have its own scheduler running in each kernel thread, using it to share that thread between the user threads assigned to it. In other words: you need something like GNU Portable Threads running in each kernel thread to dispatch the user threads assigned to that kernel thread (https://www.gnu.org/software/pth/).

Related

User level threads vs Kernel level threads

I'm aware that User Level threads are created on the User Mode( no privileges) and Kernel threads are created in the Kernel Mode( privileged).
I am also aware that Processor threads are hardware threads that operate on Kernel Threads( I hope I am correct by putting it in this way)
Here is my confusion:-
User Level threads are not recognized by the OS as they are created, maintained and destroyed on the User Level. The OS doesn't see a multithreaded process from the User Mode as being multithreaded. It treats it as a single threaded process. Therefore, this program cannot take advantage of Multiprocessing, I guess it cannot take advantage of hyperthreading as well since it appears as single threaded in the OS.
So what's the use of Multithreading in this case? I mean the computation time will still be the same🤷‍♂️.
The last question is, do POSIX thread API and OPenMP create user level threads or Kernel threads?
I know what both libraries are, please don't explain about that.
If none creates Kernel threads then how do we create a multithreaded program that takes advantage of multiprocessing?
...what's the use of Multithreading in this case?
Multithreading is older than multiprocessing. Multithreading is one model of concurrent computing. That is to say, it's a way to write a computer program in which different activities are allowed to happen independently from each other. A classic example is a multi-user network server that creates a new thread for each connected client. Each thread can talk to its own client in a simple, synchronous way even though there may be no synchrony between what the different clients want to do. You don't need to have multiple CPUs for that.
When multi-CPU computers were invented, using multiple threads to exploit them for parallel processing was a natural and obvious choice.
I mean the computation time [for a green-threaded program that cannot exploit multiple CPUs] will still be the same.
That is true, but depending on what the different activities are that the program performs concurrently, the multi-threaded version of it may be easier to read and understand* than a program that's built around a different model of concurrency.
The reason is, we all were taught to write single-threaded, synchronous code when we were beginners. We understood that we were writing instructions that "the computer" would follow. We now say "a thread" instead of saying "the computer," but otherwise, the code executed by each thread can be mostly similar to the style of code that we wrote as beginners.
Part of what makes it so simple is, that the state of each of the concurrent activities can be mostly implicit in the contexts and the local variables (i.e., the stacks) of the different threads. If you choose a different model of concurrency (e.g., an event driven model) then you may have to explicitly represent more of that state with (maybe complex) data structures.
* Easier to read but not necessarily easier to write without making subtle mistakes. But, when I started working with large teams of software developers, they taught me that I'd be reading about ten lines of code for every one line that I wrote, so "easier to read but harder to write" turns out to be a win in the long run.
Pure user level threads are (as you pointed out) not a lot of use because they don't allow you to exploit the processing capability of multiple cores within a process.
The flip-side is that pure kernel level threads will typically incur substantial overheads when switch between threads. (There are things that you can do to deal with that, but ... that's another topic.) But the upshot is that the overheads make it inefficient to preform small tasks (units of work) using kernel level threads.
Another alternative to both is a hybrid of user level and kernel level threads. For example, suppose:
each process has one kernel level thread for each physical core,
each kernel level thread can switch between a bunch of user level threads and,
switching between a user level threads is handled by a scheduler in user space.
The Java Loom project is developing a new threading model (roughly) along those lines. Classic Java threads are still kernel level threads. New virtual threads are user level threads. A Java program gets to choose whether it uses classic or virtual threads ... or both.
There is a lot of material on Loom on the web; e.g.
https://blogs.oracle.com/javamagazine/post/java-loom-virtual-threads-platform-threads
https://www.infoq.com/news/2022/05/virtual-threads-for-jdk19/
https://wiki.openjdk.org/display/loom/Main
Loom is likely to be part of the next Java release: Java 19.
I'm pretty sure that (C / C++) POSIX threads are kernel level. I don't know about OpenMPI threads, but I'd expect they are kernel level too. (They wouldn't be fit for purpose as pure user level threads.)
I have heard of hybrid threading models for C / C++, though I don't know about actual implementations. Look for articles, etcetera that talk about Threads vs Fibres.

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.

Issues in Many to Many Model in Operating System

I have read the Many to Many Model in Operating System book by "Abraham Silberschatz". In chapter 4 it says that
"The Solaris operating system supported the many to many model in versions older than Solaris 9. However,
beginning with Solaris 9, this system uses the one-to-one model."
My Question are given in the following.
May be answer mention in book and I can't understand it, and I search on internet also but can't find a useful information why shift happen?
what are the issues with many to many model?
Why shift occur from many to many model to one to one model?
I've only worked on systems that implement what some call the one-to-one model. This classification of one-to-one/many-to-many/and many-to-one IMHO is misleading.
The suggestion here is that, in the "one-to-one model," each thread really creates two threads: A kernel mode thread and a user mode thread. In reality, all that is needed a single thread that can operate in both user and kernel modes (i.e., a kernel mode stack).
The advantage a thread that can operate in both kernel and user mode (an possibly other modes as well, aka one-to-one model) is that the threads can operate completely independently and is likely simpler to implement. Each thread can enter kernel mode (e.g. perform I/O) without blocking any other thread. The alleged downside is that this requires each thread to have its own kernel mode stack.
Again, I have not worked on a many-to-one or many-to-many system but I would suspect that these are not implemented with separate kernel mode threads but rather with kernel mode data structures that are shared by multiple threads.
what are the issues with many to many model?
The clear downside to many-to-many and many-to-one is that kernel mode becomes a resource whose unavailability can cause one group of threads to block another group of threads.
Let's say you have 3 kernel mode "threads" and 10 user mode threads. If 4 user mode threads try to do I/O at the same time, the 4th thread will block until one of the first three threads complete.
It is also more complicated to implement because you would have to manage the kernel mode "threads" as resources.
Why shift occur from many to many model to one to one model?
I cannot think of any practicable advantage of one-to-many or many-to-many and can think of many disadvantages (above). That is probably why there was a shift.
That said, IMHO, this is a confusing way to explain threading.

kernel thread native thread os thread

can any one please tell me. Are all term "kernel thread", "native thread" and "Os thread" represent kernel thread? Or they are different? If they are different what is relationship among all?
There's no real standard for that. Terminology varies depending on context. However I'll try to explain the different kind of threads that I know of (and add fibers just for completeness as I've seen people call them threads).
-- Threading within the kernel
These are most likely what your kernel thread term refers to. They only exist at the kernel level. They allow (a somewhat limited) parallel execution of the kernel code itself.
-- Application threading
These are what the term thread generally means. They are separate threads of parallel execution which may be scheduled on different processors, that share the same address space and are handled as a single process by the operating system.
The POSIX standard defines the properties threads should have in POSIX compliant systems (in fact the libraries and how each library entry is supposed to behave). Windows threading model is extremely similar to the POSIX one and, AFAIK, it's safe to talk of threading in general the way I did: parallel execution that happens within the same process and can be scheduled on different processors.
-- Ancient linux threading
In the early days the linux kernel did not support threading. However it did support creating two different processes that shared the same address space. There was a project (LinuxThreads) that tried to use this to implement some sort of threading abilities.
The problem was, of course, that the kernel would still treat them as separate processes. The result was therefore not POSIX compliant. For example the treatment of signals was problematic (as signals are a process level concept). It was IN THIS VERY SPECIFIC CONTEXT that the term "native" started to become common. It refers to "native" as in "kernel level" support for threading.
With help from the kernel actual support for POSIX compliant threading was finally implemented. Today that's the only kind of threading that really deserves the name. The old way is, in fact, not real threading at all. It's a sharing of the address space by multiple processes, and as such should be referred to. But there was a time when that was referred to as threading (as it was the only thing you could do with Linux).
-- User level and Green threading
This is another context where "native" is often used to contrast to another threading model. Green threads and userl level threads are threads that do happen within the same process, but they are totally handled at userlevel. Green threads are used in virtual machines (especially those that implement pcode execution, as is the case for the java virtual machine), and they are also implemented at library level by many languages (examples: Haskell, Racket, Smalltalk).
These threads do not need to rely on any threading facilities by the kernel (but often do rely on asynchronous I/O). As such they generally cannot schedule on separate processors. In these contexts "native thread" or "OS thread" could be used to refer to the actual kernel scheduled threads in contrast to the green/user level threads.
Note that "cannot be scheduled on separate processors" is only true if they are used alone. In an hybrid system that has both user level/green threads and native/os threads, it may be possible to create exactly one native/os thread for each processor (and on some systems to set the affinity mask so that each only runs on a specific processor) and then effectively assign the userlevel threads to these.
-- Fibers and cooperative multitasking
I have seen some people call these threads. It's improper, the correct name is fibers. They are also a model of parallel execution, but contrary to threads (and processes) they are cooperative. Which means that whenever a fiber is running, the other fibers will not run until the running fiber voluntarily "yields" execution accepting to be suspended and eventually resumed later.

What is the difference between kernel threads and user threads?

What is the difference between kernel threads and user threads? Is it that kernel thread are scheduled and executed in kernel mode? What are techniques used for creating kernel threads?
Is it that user thread is scheduled, executed in user mode? Is it that Kernel does not participate in executing/scheduling user threads? When interrupts occur in executing user thread then who handles it?
Whenever, thread is created a TCB is created for each. now in case of user level threads
Is it that this TCB is created in user's address space ?
In case of switching between two user level threads who handles the context switching ?
There is a concept of multithreading models :
Many to one
One to one
Many to Many.
What are these models? How are these models practically used?
Have read few articles on this topic but still confused
Wants to clear the concept ..
Thanks in advance,
Tazim
Wikipedia has answers to most if not all of these questions.
http://en.wikipedia.org/wiki/Thread_(computer_science)
http://en.wikipedia.org/wiki/Thread_(computer_science)#Processes.2C_kernel_threads.2C_user_threads.2C_and_fibers
What is the difference between kernel threads and user threads?
Kernel threads are privileged and can access things off-limits to user mode threads. Take a look at "Ring (Computer Security)" on Wikipedia. On Windows, user mode corresponds to Ring 3, while kernel mode corresponds to Ring 0.
What are techniques used for creating kernel threads?
This is extremely dependent upon the operating system.
now in case of user level threads Is it that this TCB is created in user's address space ?
The TCB records information about a thread that the kernel uses in running that thread, right? So if it were allocated in user space, the user mode thread could modify or corrupt it, which doesn't seem like a very good idea. So, don't you suppose it's created in kernel space?
What are these models? How are these models practically used?
Wikipedia seems really clear about that.
Kernel thread means a thread that the kernel is responsible for scheduling. This means, among other things, that the kernel is able to schedule each thread on different cpus/cores at the same time.
How to use them varies a lot with programming languages and threading APIs, but as a simple illustration,
void task_a();
void task_b();
int main() {
new_thread(task_a);
new_thread(task_b);
// possibly do something else in the main thread
// wait for the threads to complete their work
}
In every implementation I am familiar with, the kernel may pause them at any time. ("pre-emptive")
User threads, or "User scheduled threads", make the program itself responsible for switching between them. There are many ways of doing this and correspondingly there is a variety of names for them.
On one end you have "Green threads"; basically trying to do the same thing as kernel threads do. Thus you keep all the complications of programming with real threads.
On the opposite end, you have "Fibers", which are required to yield before any other fiber gets run. This means
The fibers are run sequentially. There is no parallell performance gains to be had.
The interactions between fibers is very well defined. Other code run only at the exact points you yield. Other code won't be changing variables while you're working on them.
Most of the low-level complexities programmers struggle with in multithreading, such as cache coherency (looking at MT questions on this site, most people don't get that), are not a factor.
As the simplest example of fibers I can think of:
while(tasks_not_done) {
do_part_of_a();
do_part_of_b();
}
where each does some work, then returns when that part is done. Note that these are done sequentially in the same "hardware thread" meaning you do not get a performance increase from parallellism. On the other hand, interactions between them are very well defined, so you don't have race conditions. The actual working of each function can vary. They could also be "user thread objects" from some vector/array.
Essentially user threads run in the context of a user with the appropriate privilege levels e.g. user threads most certainly won't have access to kernel-level memory/data structures/routines etc. Whereas Kernel threads run in the context of the OS kernel thus giving them privileges to execute code which has access to low level kernel routines/memory/data structures.

Resources