A process is not always ready to run. Occasionally, it has to wait for events from external sources beyond its control — for keyboard input in a text editor, for example. Until the event occurs, the process cannot run.
The scheduler must know the status of every process in the system when switching between tasks; it obviously doesn't make sense to assign CPU time to processes that have nothing to do. Of equal importance are the transitions between individual process states. For example, if a process is waiting for data from a peripheral device, it is the responsibility of the scheduler to change the state of the process from waiting to runnable once the data have arrived.
A process may have one of the following states:
□ Running — The process is executing at the moment.
□ Waiting — The process is able to run but is not allowed to because the CPU is allocated to another process. The scheduler can select the process, if it wants to, at the next task switch.
□ Sleeping — The process is sleeping and cannot run because it is waiting for an external event. The scheduler cannot select the process at the next task switch.
The system saves all processes in a process table — regardless of whether they are running, sleeping, or waiting. However, sleeping processes are specially "marked" so that the scheduler knows they are not ready to run (see how this is implemented in Section 2.3). There are also a number of queues that group sleeping processes so that they can be woken at a suitable time — when, for example, an external event that the process has been waiting for takes place.
Figure 2-2 shows several process states and transitions.
Let's start our examination of the various transitions with a queued runnable process; the process is ready to run but is not allowed to because the CPU is allocated to a different process (its state is therefore "waiting"). It remains in this state until the scheduler grants it CPU time. Once this happens, its state changes to "running" (path 4).
When the scheduler decides to withdraw CPU resources from the process — I deal with the possible reasons why shortly — the process state changes from "running" to "waiting" (path 2), and the cycle starts anew. There are, in fact, two "sleeping" states that differ according to whether they can be interrupted by signals or not. At the moment, this difference is not important, but it is of relevance when we examine implementation more closely.
If the process has to wait for an event, its state changes (path 1) from "running" to "sleeping." However, it cannot change directly from "sleeping" to "running"; once the event it was waiting for has taken place, the process changes back to the "waiting" state (path 3) and then rejoins the normal cycle.
Once program execution terminates (e.g., the user closes the the application), the process state changes from "running" to "stopped" (path 5).
A special process state not listed above is the "zombie"state. As the name suggests, such processes are defunct but are somehow still alive. In reality, they are dead because their resources (RAM, connections to peripherals, etc.) have already been released so that they cannot and never will run again. However, they are still alive because there are still entries for them in the process table.
How do zombies come about? The reason lies in the process creation and destruction structure under Unix. A program terminates when two events occur — first, the program must be killed by another process or by a user (this is usually done by sending a sigterm or sigkill signal, which is equivalent to terminating the process regularly); second, the parent process from which the process originates must invoke or have already invoked the wait4 (read: wait for) system call when the child process terminates. This confirms to the kernel that the parent process has acknowledged the death of the child. The system call enables the kernel to free resources reserved by the child process.
A zombie occurs when only the first condition (the program is terminated) applies but not the second (wait4). A process always switches briefly to the zombie state between termination and removal of its data from the process table. In some cases (if, e.g., the parent process is badly programmed and does not issue a wait call), a zombie can firmly lodge itself in the process table and remain there until the next reboot. This can be seen by reading the output of process tools such as ps or top. This is hardly a problem as the residual data take up little space in the kernel.
Was this article helpful?