Pos Notes Unit-2
Pos Notes Unit-2
UNIT-II:
Process Management – Process concept, process scheduling, operations, Inter process
Communication, Multi Thread programming models. Process scheduling criteria and
algorithms, and their evaluation.
For each process there is a Process Control Block, PCB, which stores the
following ( types of ) process-specific information, as illustrated in
Figure 3.1. ( Specific details may vary from system to system. )
The two main objectives of the process scheduling system are to keep the CPU
busy at all times and to deliver "acceptable" response times for all programs,
particularly for interactive ones.
The process scheduler must meet these objectives by implementing suitable
policies for swapping processes in and out of the CPU.
( Note that these objectives can be conflicting. In particular, every time the
system steps in to swap processes it takes up time on the CPU to do so, which
is thereby "lost" from doing any useful productive work. )
2.2.2 Schedulers
There are two options for the parent process after creating the child:
1. Wait for the child process to terminate before proceeding. The parent makes
a wait( ) system call, for either a specific child or for any child, which causes
the parent process to block until the wait( ) returns. UNIX shells normally wait
for their children to complete before issuing a new prompt.
2. Run concurrently with the child, continuing to process without waiting. This
is the operation seen when a UNIX shell runs a process as a background task. It
is also possible for the parent to run for a while, and then wait for the child
later, which might occur in a sort of a parallel processing operation. ( E.g. the
parent may fork off a number of children without waiting for any of them, then
do a little work of its own, and then wait for the children. )
o child code:
int exitCode;
exit( exitCode ); // return
exitCode; has the same effect when executed from main( )
parent code:
pid_t pid;
int status
pid = wait( &status ); // pid indicates which child exited.
exitCode in low-order bits of status // macros can test the high-order bits of
status for why it stopped
When a process terminates, all of its system resources are freed up,
open files flushed and closed, etc. The process termination status and
execution times are returned to the parent if the parent is waiting for the
child to terminate, or eventually returned to init if the process becomes an
orphan. ( Processes which are trying to terminate but which cannot because
their parent is not waiting for them are termed zombies. These are
eventually inherited by init as orphans and killed off. Note that modern
UNIX shells do not produce as many orphans and zombies as older
systems used to. )
2.4 Interprocess Communication
o Information Sharing - There may be several processes which need access to the
same file for example. ( e.g. pipelines. )
o Computation speedup - Often a solution to a problem can be solved faster if the
problem can be broken down into sub-tasks to be solved simultaneously
( particularly when multiple processors are involved. )
o Modularity - The most efficient architecture may be to break a system down
into cooperating modules. ( E.g. databases with a client-server architecture. )
o Convenience - Even a single user may be multi-tasking, such as editing,
compiling, printing, and running the same code in different windows.
Cooperating processes require some type of inter-process
communication, which is most commonly one of two types: Shared Memory
systems or Message Passing systems.
Shared Memory is faster once it is set up, because no system calls are required
and access occurs at normal memory speeds. However it is more complicated
to set up, and doesn't work as well across multiple computers. Shared memory
is generally preferable when large amounts of information must be shared
quickly on the same computer.
Message Passing requires system calls for every message transfer, and is
therefore slower, but it is simpler to set up and works well across multiple
computers. Message passing is generally preferable when the amount and/or
frequency of data transfers is small, or when multiple computers are involved.
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item nextProduced;
while( true ) {
Then the consumer process. Note that the buffer is empty when "in"
is equal to "out":
item nextConsumed;
while( true ) {
3.4.2.1 Naming
With direct communication the sender must know the name of the
receiver to which it wishes to send a message.
For symmetric communication, the receiver must also know the specific name of
the sender from which it wishes to receive messages.
For asymmetric communications, this is not necessary.
Only one process can read any given message in a mailbox. Initially the process
that creates the mailbox is the owner, and is the only one allowed to read mail in
the mailbox, although this privilege may be transferred.
( Of course the process that reads the message can immediately turn around and
place an identical message back in the box for someone else to read, but that may
put it at the back end of a queue of messages. )
The OS must provide system calls to create and delete mailboxes, and to send and
receive messages to/from mailboxes.
2.4.2.2 Synchronization
Either the sending or receiving of messages ( or neither or both ) may be
either blocking or non-blocking.
2.4.2.3 Buffering
Messages are passed via queues, which may have one of three capacity
configurations:
Zero capacity - Messages cannot be stored in the queue, so senders must block
until receivers accept the messages.
Multi-Threading:
What is Thread?
A thread is a flow of execution through the process code, with its own program counter that
keeps track of which instruction to execute next, system registers which hold its current
working variables, and a stack which contains the execution history.
A thread shares with its peer threads few information like code segment, data segment and
open files. When one thread alters a code segment memory item, all other threads see that.
A thread is also called a lightweight process. Threads provide a way to improve application
performance through parallelism. Threads represent a software approach to improving
performance of operating system by reducing the overhead thread is equivalent to a classical
process.
Each thread belongs to exactly one process and no thread can exist outside a process. Each
thread represents a separate flow of control. Threads have been successfully used in
implementing network servers and web server. They also provide a suitable foundation for
parallel execution of applications on shared memory multiprocessors. The following figure
shows the working of a single-threaded and a multithreaded process.
3 In multiple processing environments, All threads can share same set of open
each process executes the same code files, child processes.
but has its own memory and file
resources.
4 If one process is blocked, then no other While one thread is blocked and
process can execute until the first waiting, a second thread in the same
process is unblocked. task can run.
5 Multiple processes without using Multiple threaded processes use fewer
threads use more resources. resources.
Advantages of Thread
Types of Thread:
Multithreading Models
Some operating system provide a combined user level thread and Kernel level thread
facility. Solaris is a good example of this combined approach. In a combined system,
multiple threads within the same application can run in parallel on multiple processors
and a blocking system call need not block the entire process. Multithreading models are
three types.
The many-to-many model multiplexes any number of user threads onto an equal or
smaller number of kernel threads.
The following diagram shows the many-to-many threading model where 6 user level
threads are multiplexing with 6 kernel level threads. In this model, developers can create
as many user threads as necessary and the corresponding Kernel threads can run in
parallel on a multiprocessor machine. This model provides the best accuracy on
concurrency and when a thread performs a blocking system call, the kernel can schedule
another thread for execution.
Many-to-one model maps many user level threads to one Kernel-level thread. Thread
management is done in user space by the thread library. When thread makes a blocking
system call, the entire process will be blocked. Only one thread can access the Kernel at
a time, so multiple threads are unable to run in parallel on multiprocessors.
If the user-level thread libraries are implemented in the operating system in such a way
that the system does not support them, then the Kernel threads use the many-to-one
relationship modes.
One to One Model
1 User-level threads are faster to create and Kernel-level threads are slower to
manage. create and manage.
CPU Scheduling
Almost all programs have some alternating cycle of CPU number crunching
and waiting for I/O of some kind. ( Even a simple fetch from memory takes
a long time relative to CPU speeds. )
In a simple system running a single process, the time spent waiting for I/O is
wasted, and those CPU cycles are lost forever.
A scheduling system allows one process to use the CPU while another is
waiting for I/O, thereby making full use of otherwise lost CPU cycles.
CPU bursts vary from process to process, and from program to program, but an extensive
study shows frequency patterns similar to that shown in Figure below
Fig:Histogram of CPU Burst Duration
CPU Scheduler
Whenever the CPU becomes idle, it is the job of the CPU Scheduler ( a.k.a. the short-term
scheduler ) to select another process from the ready queue to run next.
The storage structure for the ready queue and the algorithm used to select the next process are not
necessarily a FIFO queue. There are several alternatives to choose from, as well as numerous
adjustable parameters for each algorithm, which is the basic subject of this entire chapter.
When a process switches from the running state to the waiting state, such as for an I/O request or
invocation of the wait( ) system call.
When a process switches from the running state to the ready state, for example in response to an
interrupt.
When a process switches from the waiting state to the ready state, say at completion of I/O or a
return from wait( ).
If scheduling takes place only under conditions 1 and 4, the system is said to be non-preemptive,
or cooperative. Under these conditions, once a process starts running it keeps running, until it
either voluntarily blocks or until it finishes. Otherwise the system is said to be preemptive.
Windows used non-preemptive scheduling up to Windows 3.x, and started using pre-emptive
scheduling with Win95. Macs used non-preemptive prior to OSX, and pre-emptive since then.
Note that pre-emptive scheduling is only possible on hardware that supports a timer interrupt.
Note that pre-emptive scheduling can cause problems when two processes share data, because
one process may get interrupted in the middle of updating shared data structures. Chapter 6 will
examine this issue in greater detail.
Preemption can also be a problem if the kernel is busy implementing a system call ( e.g. updating
critical kernel data structures ) when the preemption occurs. Most modern UNIXes deal with this
problem by making the process wait until the system call has either completed or blocked before
allowing the preemption Unfortunately this solution is problematic for real-time systems, as
real-time response can no longer be guaranteed.
Some critical sections of code protect themselves from concurrency problems by disabling
interrupts before entering the critical section and re-enabling interrupts on exiting the section.
Needless to say, this should only be done in rare situations, and only on very short pieces of code
that will finish quickly, ( usually just a few machine instructions. )
Dispatcher
The dispatcher is the module that gives control of the CPU to the process selected by the
scheduler. This function involves:
Switching context.
Switching to user mode.
Jumping to the proper location in the newly loaded program.
The dispatcher needs to be as fast as possible, as it is run on every context switch. The time
consumed by the dispatcher is known as dispatch latency.
Scheduling Criteria
There are several different criteria to consider when trying to select the "best" scheduling
algorithm for a particular situation and environment, including:
CPU utilization - Ideally the CPU would be busy 100% of the time, so as to waste 0 CPU
cycles. On a real system CPU usage should range from 40% ( lightly loaded ) to 90%
( heavily loaded. )
Throughput - Number of processes completed per unit time. May range from 10 / second to
1 / hour depending on the specific processes.
Turnaround time - Time required for a particular process to complete, from submission
time to completion. ( Wall clock time. )
Waiting time - How much time processes spend in the ready queue waiting their turn to get
on the CPU. ( Load average - The average number of processes sitting in the ready queue
waiting their turn to get into the CPU. Reported in 1-minute, 5-minute, and 15-minute
averages by "uptime" and "who". )
Response time - The time taken in an interactive program from the issuance of a command
to the commence of a response to that command.
In general one wants to optimize the average value of a criteria ( Maximize CPU utilization and
throughput, and minimize all the others. ) However some times one wants to do something
different, such as to minimize the maximum response time.
Sometimes it is most desirable to minimize the variance of a criteria than the actual value. I.e.
users are more accepting of a consistent predictable system than an inconsistent one, even if it is
a little bit slower.
Scheduling Algorithms
The following subsections will explain several common scheduling strategies, looking at only a
single CPU burst each for a small number of processes. Obviously real systems have to deal with
a lot more simultaneous processes executing their CPU-I/O burst cycles.
FCFS is very simple - Just a FIFO queue, like customers waiting in line at the bank or the post
office or at a copying machine.
Unfortunately, however, FCFS can yield some very long average wait times, particularly if the
first process to get there takes a long time. For example, consider the following three processes:
P1 24
P2 3
P3 3
In the first Gantt chart below, process P1 arrives first. The average waiting time for the three
processes is ( 0 + 24 + 27 ) / 3 = 17.0 ms.
In the second Gantt chart below, the same three processes have an average wait time of ( 0 +
3 + 6 ) / 3 = 3.0 ms. The total run time for the three bursts is the same, but in the second case
two of the three finish much quicker, and the other process is only delayed by a short
amount.
FCFS can also block the system in a busy dynamic system in another way, known as
the convoy effect. When one CPU intensive process blocks the CPU, a number of I/O
intensive processes can get backed up behind it, leaving the I/O devices idle. When the CPU
hog finally relinquishes the CPU, then the I/O processes pass through the CPU quickly,
leaving the CPU idle while everyone queues up for I/O, and then the cycle repeats itself
when the CPU intensive process gets back to the ready queue.
The idea behind the SJF algorithm is to pick the quickest fastest little job that needs to be done,
get it out of the way first, and then pick the next smallest fastest job to do next.
( Technically this algorithm picks a process based on the next shortest CPU burst, not the overall
process time. )
For example, the Gantt chart below is based upon the following CPU burst times, ( and the
assumption that all jobs arrive at the same time. )
In the case above the average wait time is ( 0 + 3 + 9 + 16 ) / 4 = 7.0 ms, ( as opposed to 10.25
ms for FCFS for the same processes. )
SJF can be proven to be the fastest scheduling algorithm, but it suffers from one important
problem: How do you know how long the next CPU burst is going to be?
For long-term batch jobs this can be done based upon the limits that users set for their jobs when
they submit them, which encourages them to set low limits, but risks their having to re-submit
the job if they set the limit too low. However that does not work for short-term CPU scheduling
on an interactive system.
Another option would be to statistically measure the run time characteristics of jobs, particularly
if the same tasks are run repeatedly and predictably. But once again that really isn't a viable
option for short term CPU scheduling in the real world.
A more practical approach is to predict the length of the next burst, based on some historical
measurement of recent burst times for this process. One simple, fast, and relatively accurate
method is the exponential average, which can be defined as follows. ( The book uses tau and t for
their variables, but those are hard to distinguish from one another and don't work well in
HTML. )
In this scheme the previous estimate contains the history of all previous times, and alpha serves
as a weighting factor for the relative importance of recent data versus past history. If alpha is 1.0,
then past history is ignored, and we assume the next burst will be the same length as the last burst.
If alpha is 0.0, then all measured burst times are ignored, and we just assume a constant burst
time. Most commonly alpha is set at 0.5, as illustrated in Figure 5.3:
SJF can be either preemptive or non-preemptive. Preemption occurs when a new process arrives
in the ready queue that has a predicted burst time shorter than the time remaining in the process
whose burst is currently on the CPU. Preemptive SJF is sometimes referred to as shortest
remaining time first scheduling.
For example, the following Gantt chart is based upon the following data:
3. Priority Scheduling
Priority scheduling is a more general case of SJF, in which each job is assigned a priority and the
job with the highest priority gets scheduled first. ( SJF uses the inverse of the next expected burst
time as its priority - The smaller the expected burst, the higher the priority. )
Note that in practice, priorities are implemented using integers within a fixed range, but there is
no agreed-upon convention as to whether "high" priorities use large numbers or small numbers.
This book uses low number for high priorities, with 0 being the highest possible priority.
For example, the following Gantt chart is based upon these process burst times and priorities, and
yields an average waiting time of 8.2 ms:
Priorities can be assigned either internally or externally. Internal priorities are assigned by the OS
using criteria such as average burst time, ratio of CPU to I/O activity, system resource use, and
other factors available to the kernel. External priorities are assigned by users, based on the
importance of the job, fees paid, politics, etc.
If this problem is allowed to occur, then processes will either run eventually when the system
load lightens ( at say 2:00 a.m. ), or will eventually get lost when the system is shut down or
crashes. ( There are rumors of jobs that have been stuck for years. )
One common solution to this problem is aging, in which priorities of jobs increase the longer
they wait. Under this scheme a low-priority job will eventually get its priority raised high enough
that it gets run.
Shortest remaining time (SRT) is the preemptive version of the SJN algorithm.
The processor is allocated to the job closest to completion but it can be preempted by a newer
ready job with shorter time to completion.
Impossible to implement in interactive systems where required CPU time is not known.
It is often used in batch environments where short jobs need to give preference.
Round robin scheduling is similar to FCFS scheduling, except that CPU bursts are assigned with
limits called time quantum.
When a process is given the CPU, a timer is set for whatever value has been set for a time
quantum.
If the process finishes its burst before the time quantum timer expires, then it is swapped out of
the CPU just like the normal FCFS algorithm.
If the timer goes off first, then the process is swapped out of the CPU and moved to the back end
of the ready queue.
The ready queue is maintained as a circular queue, so when all processes have had a turn, then
the scheduler gives the first process another turn, and so on.
RR scheduling can give the effect of all processors sharing the CPU equally, although the
average wait time can be longer than with other scheduling algorithms. In the following example
the average wait time is 5.66 ms.
BUT, a real system invokes overhead for every context switch, and the smaller the time quantum
the more context switches there are. ( See Figure 5.4 below. ) Most modern systems use time
quantum between 10 and 100 milliseconds, and context switch times on the order of 10
microseconds, so the overhead is small relative to the time quantum.
Turn around time also varies with quantum time, in a non-apparent manner. Consider, for
example the processes shown in Figure 5.5:
In general, turnaround time is minimized if most processes finish their next cpu burst within one
time quantum. For example, with three processes of 10 ms bursts each, the average turnaround
time for 1 ms quantum is 29, and for 10 ms quantum it reduces to 20. However, if it is made too
large, then RR just degenerates to FCFS. A rule of thumb is that 80% of CPU bursts should be
smaller than the time quantum.
When processes can be readily categorized, then multiple separate queues can be established,
each implementing whatever scheduling algorithm is most appropriate for that type of job, and/or
with different parametric adjustments.
Scheduling must also be done between queues, that is scheduling one queue to get time relative
to other queues. Two common options are strict priority ( no job in a lower priority queue runs
until all higher priority queues are empty ) and round-robin ( each queue gets a time slice in turn,
possibly of different sizes. )
Note that under this algorithm jobs cannot switch from queue to queue - Once they are assigned a
queue, that is their queue until they finish.
Algorithm Evaluation
The first step in determining which algorithm ( and what parameter settings within that
algorithm ) is optimal for a particular operating environment is to determine what criteria
are to be used, what goals are to be targeted, and what constraints if any must be applied.
For example, one might want to "maximize CPU utilization, subject to a maximum
response time of 1 second".
Once criteria have been established, then different algorithms can be analyzed and a
"best choice" determined. The following sections outline some different methods for
determining the "best choice".
1. Deterministic Modeling
2. Queuing models
3. Simulation
4. Implementation
1. Deterministic Modelling:
If a specific workload is known, then the exact values for major criteria can be fairly
easily calculated, and the "best" determined. For example, consider the following
workload ( with all processes arriving at time 0 ), and the resulting schedules determined
by three different algorithms:
The average waiting times for FCFS, SJF, and RR are 28ms, 13ms, and 23ms
respectively.
Deterministic modeling is fast and easy, but it requires specific known input, and the
results only apply for that particular set of input. However by examining multiple similar
cases, certain trends can be observed. ( Like the fact that for processes arriving at the
same time, SJF will always yield the shortest average wait time. )
2. Queuing Models
Specific process data is often not available, particularly for future times. However a
study of historical performance can often produce statistical descriptions of certain
important parameters, such as the rate at which new processes arrive, the ratio of CPU
bursts to I/O times, the distribution of CPU burst times and I/O burst times, etc.
N = Lambda * W
Queuing models treat the computer as a network of interconnected queues, each of which
is described by its probability distribution statistics and formulas such as Little's formula.
Unfortunately real systems and modern scheduling algorithms are so complex as to make
the mathematics intractable in many cases with real systems.
3. Simulations
Statistics are gathered at each clock tick so that the system performance can be analysed.
The data to drive the simulation can be generated in the same way as the queuing model,
although this leads to similar problems.
Alternatively, we can use trace data. This is data collected from real processes on real
machines and is fed into the simulation. This can often provide good results and good
comparisons over a range of scheduling algorithms.
However, simulations can take a long time to run, can take a long time to implement and
the trace data may be difficult to collect and require large amounts of storage.
4. Implementation
The best way to compare algorithms is to implement them on real machines. This will
give the best results but does have a number of disadvantages.