10.1 Introduction
We saw in Chapter 8 that a process is associated with a set of resources including its memory segments (text, stack, initialized data, uninitialized data), environment variables and command line arguments, and various properties and data that are contained in kernel resources such as the process and user structures. A partial list of the kinds of information contained in these structures includes things such as the process’s
- IDs such as process ID, process group ID, user ID, and group ID
- Hardware state
- Memory mappings, such as where process segments are located
- Flags such as set-uid, set-gid
- File descriptors
- Signal masks and dispositions
- Resource limits
- Inter-process communication tools such as message queues, pipes, semaphores, or shared memory.
A process is a fairly heavy object in the sense that when a process is created, all of these resources must be created for it. The fork() system call duplicates some, but not all, of the calling process’s resources. Some of them are shared between the parent and child process. Processes by default are limited in what they can share with each other because they do not share their memory spaces. Thus, for example, they do not in general share variables and other objects that they create in memory. Most operating systems provide an API for sharing memory though. For example, in Linux 2.4 and later, and glibc 2.2 and later, POSIX shared memory is available so that unrelated processes can communicate through shared memory objects. Solaris also supported shared memory, both natively and with support for the later POSIX standard. In addition, processes can share files and messages, and they can send each other signals to synchronize. The biggest drawback to using processes as a means of multi-tasking is their consumption of system resources. This was the motivation for the invention of threads.
10.2 Thread Concepts
A thread is a ow of control (think sequence of instructions) that can be independently scheduled by the kernel. A typical UNIX process can be thought of as having a single thread of control: each process is doing only one thing at a time. When a program has multiple threads of control, more than one thing at a time can be done within a single process, with each thread handling a separate task. Some of the advantages of this are that
- Code to handle asynchronous events can be executed by a separate thread. Each thread can then handle its event using a synchronous programming model.
- Whereas multiple processes have to use mechanisms provided by the kernel to share memory and file descriptors, threads automatically have access to the same memory address space, which is faster and simpler.
- Even on a single processor machine, performance can be improved by putting calls to system functions with expected long waits in separate threads. This way, just the calling thread blocks, and not the whole process.
- Response time of interactive programs can be improved by splitting o threads to handle user input and output.
Threads share certain resources with the parent process and each other, and maintain private copies of other resources. The most important resources shared by the threads are the program’s text, i.e., its executable code, and its global and heap memory. This implies that threads can communicate through the program’s global variables, but it also implies that they have to synchronize their access to these shared resources. To make threads independently schedulable, at the very least they they must have their own stack and register values.
In UNIX, POSIX requires that each thread will have its own distinct
- thread ID
- stack and an alternate stack
- stack pointer and registers
- signal mask
- errno value
- scheduling properties
- thread specific data.
On the other hand, in addition to the text and data segments of the process, UNIX threads share
- file descriptors
- environment variables
- process ID
- parent process ID
- process group ID and session ID
- controlling terminal
- user and group IDs
- open file descriptors
- record locks
- signal dispositions
- file mode creation mask (the umask)
- current directory and root directory
- interval timers and POSIX timers
- nice value
- resource limits
- measurements of the consumption of CPU time and resources
To summarize, a thread
- is a single ow of control within a process and uses the process resources;
- duplicates only the resources it needs to be independently schedulable;
- can share the process resources with other threads within the process; and
- terminates if the parent process is terminated;