Threads Implementation
Kernel Threads
Kernel threads are the simplest type of threads. They are implemented in the operating system kernel itself. It's used to execute the kernel code concurrently.
Single-threaded processes with multiple kernel threads
Each process has only a single thread, however, the kernel has more than one thread, so it can execute different code concurrently. This means that the kernel can handle multiple system calls from different processes concurrently.
Multi-threaded processes with multiple kernel threads
Each process can have more than a single thread. This is enabled by the operating system itself by providing a library and system calls to allow processes of having many threads. Unfortunately, this makes a huge overhead in performance as we will discuss further.
User-level threads
By allowing the processes to have many threads at the user-level, we reduce the overhead of the system calls to the kernel. From the kernel perspective, each process is a single thread, and from the user-level perspective, each process can have many threads. However, this type of thread has many problems that we will discuss further.
Multithreading Models
One-to-One
In this model, each user-level thread is mapped to a kernel thread. This means that each thread can make system calls without any blocking on the other threads at the same process. Also, it allows us to make the best use of multicore processors. But this creates a huge overhead for the operating system because, at each thread operation, we need to do a system call to the kernel. To prevent processes from overwhelming the operating system with a huge number of system calls to maintain threads, the designers decided to restrict the number of threads supported by the OS. This model is used by Linux and Windows.
One-to-one model. Retrieved from https://cs162.org/
Many-to-One
To solve the performance issues from the one-to-one, operating systems designers tried to make many user-level threads share the same kernel thread, which means that if one user-level thread makes a system call, the other user-level threads inside the same process will be stopped. However, this problem can be solved by the Scheduler Activations. The main disadvantage of this model is that we are not making the best use of our multicore processors!
Many-to-one model. Retrieved from https://cs162.org/
Many-to-Many
Whenever software engineers face a problem, they first give their beloved patterns a try, one of these patterns is pipelining. Why create a kernel thread for each user-level thread, or to create a single kernel thread for all user-level threads, if you can create many kernel threads to represent many user-level threads that get pipelined into these different kernel threads. This model solves the previous problems. Whenever a user-level thread makes a system call, the other user-level thread at the same process can continue running, and also don't need to make a system call for each thread operation, as there are many of these operations are implemented in the user-level library. This model is used in Go runtime.
Many-to-many model. Retrieved from https://cs162.org/
References:
- Anderson, T. E., & Dahlin, M. (2011). Operating systems: Principles and practice. West Lake Hills, TX: Recursive Books.
- Silberschatz, A., Galvin, P., & Gagne, G. (2012). Operating System Concepts, 9th Edition. John Wiley & Sons.