A thread is a basic unit of CPU utilization; it comprises a thread ID, a program counter, a register set, and a stack. It shares with other threads belonging to the same process its code section, data section, and other operating-system resources, such as open files and signals. A traditional (or heavyweight) process has a single thread of control. If a process has multiple threads of control, it can perform more than one task at a time.
The unit of execution (unit of dispatching) and a collection of resources, with which the unit of execution is associated, characterize the notion of a process. A thread is the abstraction of a unit of execution. It is also referred to as a lightweight process (LWP).
As a basic unit of CPU utilization, a thread consists of an instruction pointer (also referred to as the PC or instruction counter), a CPU register set, and a stack. A thread shares its code and data, as well as system resources and other OS-related information, with its peer group (other threads of the same process).
A good example of an application that could make use of threads is a file server on a local area network (LAN).
A ‘‘controller’’ thread accepts file service requests and spawns a ‘‘worker’’ thread for each request, therefore may handle many requests concurrently. When a worker thread finishes servicing a request, it is destroyed.
Threads versus processes
- A thread operates in much the same way as a process:
- can be one of the several states;
- executes sequentially (within a process and shares the CPU);
- can issue system calls.
- Creating a thread is less expensive.
- Switching to a thread within a process is cheaper than switching between threads of different processes.
- Threads within a process share resources (including the same memory address space) conveniently and efficiently compared to separate processes.
- Threads within a process are NOT independent and are NOT protected against each other.
Thread implementations
- User level: Implemented as a set of library functions; cannot be scheduled independently; each thread gets partial time quantum of a process; a system call by a thread blocks the entire set of threads of a process; less costly (thread) operations
- Kernel level: Implemented as system calls; can be scheduled directly by the OS; independent operation of threads in a single process; more expensive (thread) operations.
- Hybrid approach: Combines the advantages of the above two; e.g., Solaris threads.
Thread in OS
1. Windows Threads
Windows implements the Windows API, which is the primary API for the family of Microsoft operating systems (Windows 98, NT, 2000, and XP, as well as Windows 7). Indeed, much of what is mentioned in this section applies to this entire family of operating systems. A Windows application runs as a separate process, and each process may contain one or more threads.
The general components of a thread include:
- A thread ID uniquely identifies the thread
- A register set representing the status of the processor
- A user stack, employed when the thread is running in user mode, and a kernel stack, employed when the thread is running in kernel mode
- A private storage area used by various run-time libraries and dynamic link libraries (DLLs)
The register set, stacks, and private storage area are known as the context of the thread. The primary data structures of a thread include:
- E-THREAD—executive thread block
- K-THREAD—kernel thread block
- TEB—thread environment block
2. Linux Threads
Linux provides the fork() system call with the traditional functionality of duplicating a process. Linux also provides the ability to create threads using the clone() system call. However, Linux does not distinguish between processes and threads. In fact, Linux uses the term task rather than process or thread when referring to a flow of control within a program.
When clone() is invoked, it is passed a set of flags that determine how much sharing is to take place between the parent and child tasks. For example, suppose that clone() is passed the flags CLONE FS, CLONE VM, CLONE SIGHAND, and CLONE FILES. The parent and child tasks will then share the same file-system information (such as the current working directory), the same memory space, the same signal handlers, and the same set of open files. Using clone() in this fashion is equivalent to creating a thread since the parent task shares most of its resources with its child task. However, if none of these flags is set when clone() is invoked, no sharing takes place, resulting in functionality similar to that provided by the fork() system call.