HPlogo HP-UX Process Management: White Paper > Chapter 1 Process Management

What are Kernel Threads?

» 

Technical documentation

Complete book in PDF

 » Table of Contents

A process is a representation of an entire running program. By comparison, a kernel thread is a fraction of that program. Like a process, a thread is a sequence of instructions being executed in a program. Kernel threads exist within the context of a process and provide the operating system the means to address and execute smaller segments of the process. It also enables programs to take advantage of capabilities provided by the hardware for concurrent and parallel processing.

The concept of threads is interpreted numerous ways, but to quote a definitive source on the HP-UX implementation (S.J. Norton and M.D. DiPasquale, `ThreadTime: Multithreaded Programming Guide, (Upper Saddle River, NJ: Prentice Hall PTR, Hewlett-Packard Professional Books), 1997, p.2):

A thread is "an independent flow of control within the process", composed of a [process's register] context, program counter, and a sequence of instructions to execute. An independent flow of control is an execution path through the program code in a process. The register context and program counter contain values that indicate the current state of program execution. The sequence of instructions to execute is the actual program code.

Further, threads are

  • A programming paradigm and associated set of interfaces allowing applications to be broken up into logically distinct tasks that when supported by hardware, can be run in parallel.

  • Multiple, independent, executable entities within a process, all sharing the process' address space, yet owning unique resources within the process.

Each thread can be scheduled, synchronized, prioritized, and can send and receive signals. Threads share many of the resources of a process, eliminating much of the overhead involved during creation, termination, and synchronization.

A thread's "management facilities" (register context et al) are used to maintain the thread's "state" information throughout its lifetime. State information monitors the condition of an entity (like a thread or process); it provides a snap-shot of an entity's current condition. For example, when a thread context switch takes place, the newly scheduled thread's register information tells the processor where the thread left off in its execution. More specifically, a thread's program counter would contain the current instruction to be executed upon start up.

As of release 10.30, HP-UX has kernel threads, which change the role of processes. A process is now just a logical container used to group related threads of an application. Each process contains at least one thread. This single (initial) thread is created automatically by the system when the process starts up. An application must explicitly create the additional threads.A process with only one thread is a "single-threaded process." A process with more than one thread is a "multi-threaded process." Currently, the HP-UX kernel manages single-threaded processes as executable entities that can be scheduled to run on a processor (that is, each process contains only one thread.) Development of HP-UX is moving toward an operating system that supports multi-threaded processes.

Comparison of Threads and Processes

The following lists process resources shared by all threads within a process:

  • File descriptors, file creation mask

  • User and group IDs, tty

  • Root working directory, current working directory

  • semaphores, memory, program global variables

  • signal actions, message queues, timers

The following lists thread resources private to each thread within a process:

  • User registers

  • Error number (errno)

  • Scheduling policy and priority

  • Processor affinity

  • Signal mask

  • Stack

  • Thread-specific data

  • Kernel uarea

Like the context of a process, the context of a thread consists of instructions, attributes, user structure with register context, private storage, thread structure, and thread stack.

Two kernel data structures -- proc and user -- represent every process in a process-based kernel. (The proc structure is non-swappable and user is swappable.) In addition, each process has a kernel stack allocated with the user structure in the uarea.

A threads-based kernel also uses a proc and a user structure. Like the proc structure of the process-based kernel, the threads-based proc structure remains memory resident and contains per-process data shared by all the kernel threads within the process.

Each thread shares its host process' address space for access to resources owned or used by the process (such as a process' pointers into the file descriptor table). Head and tail pointers to a process' thread list are included in the proc structure.

Each thread manages its own kernel resources with private data structures to maintain state information and a unique counter. A thread is represented by a kthread structure (always memory resident), a user structure (swappable), and a separate kernel stack for each kernel thread.

Every kthread structure contains a pointer to its associated proc structure, a pointer to the next thread within the same process. All the active threads in the system are linked together on the active threads list.

Like a process, a thread has a kind of life cycle based on the execution of a program or script. Through the course of time, threads like processes are created, run, sleep, are terminated.

Figure 1-5 Typical thread state sequence

[Typical thread state sequence]

User and Kernel Mode

A kernel thread, like a process, operates in user and kernel modes, and through the course of its lifetime switches between the stacks maintained in each mode. Stacks for each mode accumulate information such as variables, addresses, and buffer counts accumulate and it is through these stacks that the thread executes instructions and switches modes.

Certain kinds of instructions trigger mode changes. For example, when a program invokes a system call, the system call stub code passes the system call number through a gateway page that adjusts privilege bits to switch to kernel mode. When a thread switches mode to the kernel, it executes kernel code and uses the kernel stack.

Thread's Life Cycle

Like the process, the thread can be understood in terms of its "life cycle", shown in the figure below. Thread state and flag codes are defined in kthread_private.h.

Figure 1-6 Thread life cycle

[Thread life cycle]
  1. Process is created via a call to fork() or vfork(); the fork1() routine sets up the process's pid (process id) and tid (thread id). The process and its thread are linked to the active list. The thread is given a creation state flag of TSIDL.

  2. fork1() calls newproc() to create the thread and process, and to set up the pointers to the parent. newproc() calls procdup() to create a duplicate copy of the parent and allocate the uarea for the new child process. The new child thread is flagged runnable and given a flag of TSRUN. Once the thread has this flag, it is placed in the run queue.

  3. The kernel schedules the thread to run; its state becomes TSRUNPROC (running). While in this state, the thread is given the resources it requests. This continues until a clock interrupt occurs, or the thread relinquishes its time to wait for a requested resource, or the thread is preempted by another (higher priority) thread. If this occurs, the thread's context is switched out.

  4. A thread is switched out if it must wait for a requested resource. This causes the thread to go into a state of TSLEEP. The thread sleeps until its requested resource returns and makes it eligible to run again. During the thread's TSLEEP state, the kernel calls hardclock() every click tick (10ms) to charge the currently running thread with cpu usage. After 4 clock ticks (40ms), hardclock() calls setpri() to adjust the thread's user priority. The thread is given this value on the next context switch. After 10 click tics (100ms), a context switch occurs. The next thread to run will be the threadwith the highest priority in a state of TSRUN. For the remaining threads in TSRUN state, schedcpu() is called after 100 clock tics (1 second). schedcpu() adjusts all thread priorities at this time.

  5. Once a thread acquires the requested resource, it calls the wakeup() routine and again changes states from TSLEEP to TSRUN. This makes the thread eligible to run again.

  6. On the next context switch the thread is allowed to run, provided it is the next eligible candidate. When allowed to run, the thread state changes again to TSRUNPROC.

  7. Once the thread completes its task it calls exit(). It releases all resources and transfers to the TSZOMB state. Once all resources are released, the thread and the process entries are released.

  8. If the thread is being traced, it enters the TSSTOP state.

  9. Once the thread is resumed, it transfers from TSSTOP to TSRUN.