FUNDAMENTAL OS REQUIREMENTS
Always keep in mind that the fundamental purpose of any operating system is to load and execute programs. This is true regardless of the specific goals, design features, and complexity of the particular operating system that you happen to be looking at. With this fundamental idea in mind, look again at the various functions that are provided within the operating system.
Recall that to load and execute a program, the system must provide a method of getting the program from its storage location on some I/O device, such as disk, into memory; it must provide locations in memory for the program and its data; it must provide CPU time for the program to execute; and it must provide access to the I/O facilities that the program needs during its execution. Since multiple programs are normally sharing the system and its resources, it must do all this in a way that is fair and meets the sometimes conflicting requirements of the different programs. The lower layers of the model provide programs that fulfill these requirements. The file manager layer translates logical file requests from the command shell or the user’s programs into specific physical I/O requests that are then performed by the appropriate I/O device management programs. Resource allocation management is also provided in this layer to resolve conflicts between different programs that may require I/O services at the same time. The I/O device management and resource allocation programs are sometimes known collectively as an I/O control system, or more commonly, IOCS. The memory management and scheduling operations within the resource allocation function determine if it is possible to load programs and data into memory, and, if so, where in memory the program is to be loaded. Once the program is in memory, the scheduler allocates time for the program to execute. If there are multiple programs in memory, the scheduler attempts to allocate time for each of them in some fair way. To increase security, many operating systems construct these programs as a hierarchy in which each layer of programs in the model requests services from the next innermost layer, using an established calling procedure. Most modern computers provide special protected hardware instructions for this purpose.
There are many different ways of
performing each of these functions, each with advantages and disadvantages. The
trade-offs selected reflect the design goals of the particular system. To give
you a simple example, a computer that operates strictly in a batch mode might
use a simple CPU scheduling algorithm that allows each program to run without
interruption as long as the program does not have to stop processing to wait
for I/O. This strategy would not be acceptable on an interactive system that
requires fast screen response when a user clicks the mouse or types something
into the keyboard. Or, in the case of a smart phone, when the phone rings! In the
latter cases, a more sophisticated scheduling algorithm is clearly required.
A Simple Multitasking Operating System
The miniature operating system (hereafter
referred to as MINOS) is an extremely small and simple multitasking system with
many of the important internal features of larger systems. It is based on a
real operating system that was developed by the author in the 1970s for a very early
and primitive microcomputer that was used primarily to measure data in remote
rural locations. Calculations were performed on the data and the results telecommunicated
back to a larger computer for further processing.
The original goals of the design were:
1. First and foremost, simplicity. Memory was very expensive in those days, so we didn’t want to use much for the operating system. There was only 8KB of memory in the machine.
2. Real-time support for one very important program that was run frequently and had to operate very fast. This was the data measurement program. The system therefore features a priority scheduling system in choosing which program is to run.
The internal design of MINOS was of more
interest and importance to the designers than the user interface or the file
system. There was no disk on this computer, only an audio cassette tape
recorder, modified to hold computer data, so the file system was simple. (Disks
were too expensive, too large, and too fragile for this type of system back
then!) There was a keyboard/printer user interface, but no video display
interface. Security was not a concern. The features of particular interest to
us here are the operation of memory management, process scheduling, and
dispatching. Despite their simplicity, the design of these modules is characteristic
of the way current operating systems work.
These were the important specifications for MINOS:
1. Keyboard/printer command line user interface. To keep things simple, there were only a few commands, most of which could be entered by typing a single character. For example, the letter “l” was used to load a program from tape, the letter “s” to save a program to tape.
2. Memory was divided into six fixed partitions of different sizes. A memory map is One partition was reserved for MINOS, which was entirely memory resident. Partition P-1 was reserved for high-priority programs, most commonly the data retrieval program, since it had to retrieve data in real time. Partitions P-2, P-3, and P-4 were of different sizes, but all shared equal, middle priority. Partition P-5 was a low-priority area, which was used for background tasks, mostly internal system checking, but there was a simple binary editor available that could be loaded into the low-priority partition for debugging and modifying programs.
3. The operating system was divided into three levels: the command interface; the I/O subsystem; and the kernel, which contained the memory manager, the communication interface, and the scheduler. The operating system kernel had the highest priority by default, since it had to respond to user commands and provide dispatching services. It could interrupt and preempt other programs. However, routine operations such as program loading were processed at the lowest priority level.
STARTING THE COMPUTER SYSTEM: THE BOOTSTRAP
As a first step, we need to
consider what is required to get the computer started. You will recall that
when the computer is first turned on, the contents of RAM are unknown.
Furthermore, you know that there must be a program in memory for CPU execution
to take place. These two considerations are contradictory; therefore, special
means must be included to get the system into an operating state. Initial
program loading and start-up is performed by using a bootstrap program that
is built permanently into a read-only part of memory for the computer. This bootstrap
program begins execution as soon as the machine is powered up. The bootstrap
program contains a program loader that automatically loads a selected program
from secondary storage into normal memory and transfers control to it. The
process is known as bootstrapping, or more simply, as booting the computer. IBM calls the process Initial Program Load,
or IPL. Since the bootstrap is
a read-only program, the program that it loads must be predetermined and must
be found in a known secondary storage location. On a personal computer, this
will usually be found at a particular track and sector on a hard disk, although
the bootstrap can be tailored to start the computer from another device, or
even from another computer if the system is connected to a network. On a tablet
or smart phone, the program will be found at a known location of built-in
read-only memory. Usually the bootstrap loads a program that is itself capable
of loading programs. (This is the reason that the initial program loader is called
a bootstrap.) Ultimately, the program loaded contains the operating system
kernel. In other words, when the boot procedure is complete, the kernel is
loaded, and the computer is ready for normal operation. The resident operating
system services are present and ready to go. Commands can be accepted, and
other programs loaded and executed. The bootstrap operation is usually
performed in two or more stages of loading to increase flexibility in the
location of the kernel and to keep the initial bootstrap program small.
PROCESSES AND THREADS
When considering a multitasking
system, it is easiest to think of each executing task as a program. This
representation is not inaccurate, but it is not sufficiently inclusive,
precise, or general to explain all the different situations that can occur
within a computer system. Instead, we may define each executing task more
usefully as a process. A process is defined to include a program,
together with all the resources that are associated with that program as it is
executed. Those resources may include I/O devices that have been assigned to
the particular process, keyboard input data, files that have been opened,
memory that has been assigned as a buffer for I/O data or as a stack, memory
assigned to the program, CPU time, and many other possibilities. Another way of
viewing a process is to consider it as a program in execution. A program is
viewed passively: it’s a file or a listing, for example. A process is viewed
actively: it is being processed or executed.
In batch systems, a different
terminology is sometimes used. A user submits a job to the system for
processing; the job is made up of job steps, each of which represents a
single task. It is not difficult to see the relationship among jobs,
tasks, and processes. When the job is admitted to the system, a process is
created for the job. Each of the tasks within the job also represents processes;
specifically, processes that will be created as each step in the job is
executed. In this book we tend to use the words job, task, and process
interchangeably.
The difference between a program
and a process is not usually important in normal conversation, but from the
perspective of the operating system the difference may be quite significant and
profound. For example, most modern operating systems have the capability of
sharing a single copy of a program such as an editor among many processes
concurrently. Each process has its own files and data. This practice can save
memory space, since only a single copy of the program is required, instead of many;
thus, this technique increases system capability. Crucial to this concept,
however, is the understanding that each process may be operating in a different
part of the program; therefore, each process maintains a different program
counter value during its execution time, as well as different data. Each process
has its own space to store its register values for context switching. By maintaining
a separate process for each user, the operating system can keep track of each
user’s requirements in a straightforward manner.
Even in a single-user system,
multiple processes may share program code. For example, the program code that
produces the Windows interface will be shared by all the processes with open
windows on a screen. Each process will have its own data: the coordinates of
the window, pointers to the menu structure for that window, and so forth. To the
operating system, the basic unit of work is a process. When a process is
admitted to the system, the operating system is responsible for every aspect of
its operation. The operating system must allocate initial memory for it and
must continue to assure that memory is available to the process as it is
needed. It must assign the necessary files and I/O devices and provide stack
memory and buffers. It must schedule CPU execution time for the process and
perform context switching between the various executing processes. The
operating system must maintain the integrity of the process. Finally, when the
process is completed, it terminates the process in an orderly way and restores
the system facilities and resources to make them available to other processes. Processes
that do not need to interact with any other processes are known as independent
processes. In modern systems, many processes will work together. They
will share information and files. A large task will often be modularized by
splitting it into sub tasks, so that each process will only handle one aspect of
the task. Processes that work together are known as cooperating processes.
The operating system provides mechanisms for synchronizing and communicating between
processes that are related in some way. (If one process needs the result from
another, for example, it must know when the result is available so that it can
proceed. This is known as synchronization. It must also be able to receive
the result from the other process. This is communication.)
The operating system acts as the
manager and conduit for these inter process events. To keep track of
each of the different processes that are executing concurrently in memory, the
operating system creates and maintains a block of data for each process in the
system.
A Typical Process Control Block
Process
ID |
Pointer
to parent process |
Pointer
area to child processes |
Register
save area |
Process
state |
Program
counter |
Memory
pointers |
Priority
information |
Accounting
information |
Pointers
to shared memory |
areas,
shared processes and |
libraries,
files, and |
other
I/O resources |
This data block is known as a process
control block, frequently abbreviated as PCB. The process control
block contains all relevant information about the process. It is the central
resource used by the various operating system modules as they perform their
process-related functions. In MINOS, the process control block was simple. It
was only necessary to keep track of the program counter and a pair of register values
so that processes could be suspended and restarted, plus the status and
priority of the program. Since MINOS divided memory into partitions of fixed
size, there was exactly one process and therefore one PCB per partition, so it
was not even necessary for the operating system to keep track of the memory
limits of a process. In a larger system, process control is considerably more
complex. There may be many more processes. Contention for the available memory and
for various I/O resources is more likely. There may be requirements for
communication between different processes. Scheduling and dispatch are more
difficult. The complexity of the system requires the storing of much additional
information about the process, as well as more formal control of process
operations.
Process Creation
A little thought should make it
clear to you that a process is created when you issue a command that requests
execution of a program, either by double-clicking on an icon or by typing an appropriate
command. There are also many other ways in which a process is created.
Particularly on interactive systems, process creation is one of the fundamental
tasks performed by the operating system. Processes in a computer system are
continually being created and destroyed. Since any executing program is
a process, almost any command that you enter into a multitasking interactive
system normally creates a process. Even logging in creates a process, since
logging in requires providing a program that serves as your interface, giving
you a prompt or GUI, monitoring your keystrokes, and responding to your
requests. In many systems, this is known as a user process. In some
systems, all processes that are not modules of the operating system are known
as user processes.
It should also be remembered that
the operating system itself is made up of program modules. These modules, too,
must share the use of the CPU to perform their duties. Thus, the active parts
of the operating system are, themselves, processes. When a process requests I/O
or operating system services, for example, processes are created for the
various operating system program modules that will service the request, as well
as for any additional processes resulting from the request. These processes are
sometimes known as system processes.
Process States
Most operating systems define three
primary operating states for a process. These are known as the ready state,
the running state, and the blocked state. Once a process has been
created and admitted to the system for execution, it is brought into the ready
state, where it must compete with all other processes in the ready state
for CPU execution time. Being in the ready state simply means that a process is
capable of execution if given access to the CPU. At some point in time,
presumably, the process will be given time for execution. The process is moved
from the ready state to the running state. Moving from the ready state
to the running state is called dispatching the process. During the time
that the process is in the running state, the program has control of the CPU
and is able to execute instructions. Of course, only one process can be in the
running state at a time for a uniprocessor system. If there are multiple
processors or a cluster under the operating system’s control, the OS is
responsible for dispatching a process to run in each available CPU. In a
typical multitasking system, there may be many processes in blocked or ready states at any
given time. When I/O or other services are required for the continuation of
program execution, the running process can no longer do any further useful work
until its requirement is satisfied. Some operating systems will suspend the
program when this occurs; others will allow the program to remain in the
running state, even though the program is unable to proceed. In the latter
case, most well-designed programs will suspend themselves, unless the
interruption is expected to be extremely brief. This state transition is known
as blocking, and the process remains in a blocked state until its I/O
requirement is complete. When the I/O operation is complete, the operating system
moves the process from the blocked state back to the ready state. This state
transition is frequently called wake-up. Blocking can also occur when a
process is waiting for some event other than I/O to occur, for example, a
completion signal or a data result from another process.
Threads
It is common in modern systems to
provide capability for a sort of miniprocess, known as a thread. A
thread represents a piece of a process that can be executed independently of
other parts of the process. (Think of the spell-checker in a word processor
that checks words as you type, for example.) Each thread has its own context,
consisting of a program counter value, register set, and stack space, but shares
program code, and data, and other system resources such as open files with the
other member threads in the process. Threads can operate concurrently. Like processes, threads can be created and
destroyed and can be in ready, running, and blocked states. Context switching
among threads is easier for the operating system to manage because there is no
need to manage memory, files, and other resources and no need for
synchronization or communication within the process, since this is handled
within the process itself. This advantage suggests, however, that more care
needs to be taken when the program is written, to assure that threads do not
interact with each other in subtle ways that can create conditions that cause
the program to fail. Note that there is no protection among the threads of a
process, since all the threads are using the same program code and data space. Some
systems even provide a mechanism for context switching of threads independent
of the process switching mechanism. This means that in these systems threads
can be switched without the involvement of the operating system kernel. If a
process becomes I/O blocked, it cannot proceed until the block is resolved. On
the other hand, if a thread becomes blocked, other threads in the process may
be able to continue execution within the process’s allotted time, resulting in
more rapid execution. Because the inner layers of the operating system are not even
aware of thread context switching in these systems, thread switching is
extremely rapid and efficient. Threads in these systems are commonly known as user-level
threads. Threads came about as a result of the advent of event-driven
programs. In older programs with traditional text-based displays and
keyboard input, there was a single flow of control. Event-driven programs
differ in that the flow of control depends in a much more dramatic way on user
input. With a modern graphical user interface, a user can pull down a menu and
select an action to be performed at almost any time. Selecting an item from a
menu or clicking a mouse in a particular place in a particular way is known as
an event. The program must be able to respond to a variety of different
events, at unknown times, and in unknown order of request. Most such events are
too small to justify creation of a new process. Instead, the action for each
event is treated as a thread. The thread can be executed independently, but
without the overhead of a process. There is no control block, no separate
memory, no separate resources.
The primary requirement for a
thread is a context storage area to store the program counter and registers
when context switching takes place. A very simple thread control block is
adequate for this purpose. Threads are processed in much the same way as processes.
0 Comments