Implementing Threads in the Kernel

Implementing Threads in the Kernel

Now let us examine having the kernel know about and manage the threads. No run-time system is required in each, as shown in Fig. 1-(b) of previous article "Implementing Threads in User Space". Also, there is no thread table in each process. Instead, the kernel has a thread table that keeps track of all the threads in the system. When a thread wants to create a new thread or destroy an existing thread, it makes a kernel call, which then does the creation or destruction by updating the kernel thread table.

The kernel's thread table holds each thread's registers, state, and other information. The information is the same as with user-level threads, but now kept in the kernel instead of in user space (inside the run-time system). This information is a subset of the information that traditional kernels keep up about their single threaded processes, that is, the process state. In addition, the kernel also maintains the traditional process table to keep track of processes.

All calls that might block a thread are implemented as system calls, at significantly greater cost than a call to a run-time system procedure.  When a thread blocks, the kernel, at its option, can run either another thread from the same process (if one is ready) or a thread from a  different process. With user-level threads, the run-time system keeps running threads from its own process until the kernel takes the CPU away from it (or there are no ready threads left to run).

By reason of comparatively greater cost of creating and destroying threads in the kernel, some systems take an environmentally correct approach and recycle their threads. When a thread is destroyed, it is marked as not runnable, but its kernel data structures are not otherwise affected. Later, when a new thread must be created, an old thread is reactivated, saving some overhead. Thread recycling is also possible for user-level threads, but as the thread management overhead is much smaller, there is less incitement to do this.

Kernel threads do not need any new, nonblocking system calls. Moreover, if one thread in a process causes a page fault, the kernel can  easily check to see if the process has any other runnable threads, and if so, run one of them while waiting for the required page to be  brought in from the disk. Their main drawback is that the cost of a system call is considerable, so if thread operations (creation, termination, etc.) are common, much more overhead will be incurred.

While kernel threads solve some problems, they do not solve all problems. For instance, what happens when a multithreaded process  forks? Does the new process have as many threads as the old one did, or does it have just one? In many cases, the best choice depends on what the process is planning to do next. If it is going to call exec to start a new program, most likely one thread is the correct choice, but if it continues to perform, reproducing all the threads is maybe the right thing to do.

One more issue is signals. Remember that signals are sent to processes, not to threads, at least in the traditional model. When a signal comes in, which thread should handle it? Possibly threads could register their interest in certain signals, so when a signal came in it would be given to the thread that said it wants it. But what happens if two or more threads register for the same signal. These are only two of the problems threads introduce, but there are more.


threads, process, system calls