Proccess and CPU Scheduling
Proccess and CPU Scheduling
Process Concept
A process is an instance of a program in execution.
Batch systems work in terms of "jobs". Where as in Time Sharing Systems has User Programs or
Tasks.
Many modern process concepts are still expressed in terms of jobs, ( e.g. job scheduling ), and the two terms
are often used interchangeably.
The Process
A program is a passive entity, such as a file containing a list of instructions stored on disk (often called an
executable file). In contrast, a process is an active entity, with a program counter specifying the next instruction
to execute and a set of associated resources.
A program becomes a process when an executable file is loaded into memory. Two common
techniques for loading executable files are double-clicking an icon representing the
executable file and entering the name of the executable file on the command line (as in
prog.exe or a.out).
A process itself can be an execution environment for other code. For example, to run the
compiled Java program Program.class, we would enter
java Program
The command java runs the JVM as an ordinary process, which in turns executes the Java
program Program in the virtual machine.
Process State
As a process executes, it changes state. The state of a process is defined in part by the
current activity of that process. Processes may be in one of 5 states, as shown in Figure
below.
o New - The process is in the stage of being created.
o Ready - The process has all the resources available that it needs to run,
but the CPU is not currently working on this process's instructions.
o Running - The CPU is working on this process's instructions.
o Waiting - The process cannot run at the moment, because it is waiting for
some resource to become available or for some event to occur. For example
the process may be waiting for keyboard input, disk access request, inter-
process messages, a timer to go off, or a child process to finish.
o Terminated - The process has completed.
I)Process Creation
During the course of execution, a process may create several new processes. As
mentioned earlier, the creating process is called a parent process, and the new processes
are called the children of that process. Each of these new processes may in turn create
other processes, forming a tree of processes.
Most operating systems (including UNIX, Linux, and Windows) identify processes
according to a unique process identifier (or pid), which is typically an integer number.
The pid provides a unique value for each process in the system, and it can be used as an
index to access various attributes of a process within the kernel. Figure illustrates a typical
process tree for the Linux operating system, showing the name of each process and its
pid.
In addition to supplying various physical and logical resources, the parent process may pass
along initialization data (input) to the child process. For example, consider a process whose
function is to display the contents of a file —say, image.jpg—on the screen of a terminal.
When the process is created, it will get, as an input from its parent process, the name of the
file image.jpg. Using that file name, it will open the file and write the contents out.
When a process creates a new process, two possibilities for execution exist:
There are also two address-space possibilities for the new process:
1. The child process is a duplicate of the parent process (it has the same program and data as
the parent).
2. The child process has a new program loaded into it.
For example in UNIX operating system A new process is created by the fork() system call.
Then the exec() system call loads a binary file into memory (destroying the memory image
of the program containing the exec() system call) and starts its execution. The parent can then
create more children; or, if it has nothing else to do while the child runs, it can issue a wait()
system call to move itself off the ready queue until the termination of the child
When the child process completes (by either implicitly or explicitly invoking exit()), the
parent process resumes from the call to wait(), where it completes using the exit() system call.
This is also illustrated in Figure
A process terminates when it finishes executing its final statement and asks the operating
system to delete it by using the exit() system call. All the resources of the process—
including physical and virtual memory, open files, and I/O buffers—are deallocated by
the operating system.
Termination can occur in other circumstances as well. A process can cause the termination
of another process via an appropriate system call (for example, TerminateProcess() in
Windows).
Usually, such a system call can be invoked only by the parent of the process that is to be
terminated. Otherwise, users could arbitrarily kill each other’s jobs. Note that a parent
needs to know the identities of its children if it is to terminate them. Thus, when one
process creates a new process, the identity of the newly created process is passed to the
parent.
A parent may terminate the execution of one of its children for a variety of reasons, such as
these:
• The child has exceeded its usage of some of the resources that it has been
allocated. (To determine whether this has occurred, the parent must have a mechanism
to inspect the state of its children.)
• The task assigned to the child is no longer required.
• The parent is exiting, and the operating system does not allow a child to
continue if its parent terminates.
Interprocess Communication
Independent Processes operating concurrently on a systems are those that can
neither affect other processes or be affected by other processes.
Cooperating Processes are those that can affect or be affected by other processes.
Cooperating processes require some type of inter-process communication, which is
most commonly one of two types: Shared Memory systems or Message Passing
systems. Figure illustrates the difference between the two systems
Producer-Consumer Example Using Shared Memory
This is a classic example, in which one process is producing data and another process is
consuming the data. ( In this example in the order in which it is produced, although that
could vary. )
The data is passed via an intermediary buffer, which may be either unbounded or bounded.
With a bounded buffer the producer may have to wait until there is space available in the
buffer, but with an unbounded buffer the producer will never need to wait. The consumer
may need to wait in either case until there is data available.
This example uses shared memory and a circular queue. Note in the code below that only
the producer changes "in", and only the consumer changes "out", and that they can never
be accessing the same array location at the same time.
First the following data is set up in the
shared memory area: #define
BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[
BUFFER_SIZE
]; int in = 0;
int out = 0;
Message-Passing Systems
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.
Message passing provides a mechanism to allow processes to communicate and to
synchronize their actions without sharing the same address space. It is particularly
useful in a distributed environment, where the communicating processes may reside
on different computers connected by a network. For example, an Internet chat program
could be designed so that chat participants communicate with one another by
exchanging messages.
A message-passing facility provides at least two operations
Send (message) receive(message)
If processes P and Q want to communicate, they must send messages to and receive
messages from each other: a communication link must exist between them. This link can
be implemented in a variety of ways. Here are several methods for logically implementing
a link and the send()/receive() operations:
This scheme exhibits symmetry in addressing; that is, both the sender process and the
receiver process must name the other to communicate. A variant of this scheme employs
asymmetry in addressing. Here, only the sender names the recipient; the recipient is not
required to name the sender. In this scheme, the send() and receive() primitives are defined
as follows:
send(P, message)—Send a message to process P.
receive(id, message)—Receive a message from any process.
The variable id is set to the name of the process with which communication has taken
place.
indirect communication, the messages are sent to and received from mailboxes, or ports. A
mailbox can be viewed abstractly as an object into which messages can be placed by processes
and from which messages can be removed. Each mailbox has a unique identification. A
process can communicate with another process via a number of different mailboxes, but two
processes can communicate only if they have a shared mailbox. The send() and receive()
primitives are defined as follows:
Synchronization
Communication between processes takes place through calls to send() and receive()
primitives. There are different design options for implementing each primitive. Message
passing may be either blocking or nonblocking—also known as synchronous and
asynchronous.
• Blocking send. The sending process is blocked until the message is received by the
receiving process or by the mailbox.
• Nonblocking send. The sending process sends the message and resumes operation.
• Blocking receive. The receiver blocks until a message is available.
• Nonblocking receive. The receiver retrieves either a valid message or a null.
Buffering
Whether communication is direct or indirect, messages exchanged by communicating
processes reside in a temporary queue. Basically, such queues can be implemented in three
ways:
• Zero capacity. The queue has a maximum length of zero; thus, the link cannot have any
messages waiting in it. In this case, the sender must block until the recipient receives the
message.
• Bounded capacity. The queue has finite length n; thus, at most n messages can reside in
it. If the queue is not full when a new message is sent, the message is placed in the queue
(either the message is copied or a pointer to the message is kept), and the sender can continue
execution without waiting. The link’s capacity is finite, however. If the link is full, the
sender must block until space is available in the queue.
• Unbounded capacity. The queue’s length is potentially infinite; thus, any number of
messages can wait in it.
Thread
A thread is a basic unit of CPU utilization or it is a light weight process each light
weight process is said to be a Thread, consisting of a program counter(Keeps track
of which instruction to execute next), a stack(execution History), and a set of
registers (holds its current working variables), (and a thread ID.)
Traditional (heavy weight) processes have a single thread of control - There is one
program counter, and one sequence of instructions that can be carried out at any
given time.
As shown in Figure, multi-threaded applications have multiple threads within a
single process, each having their own program counter, stack and set of registers,
but sharing common code, data, and certain structures such as open files.
Threads are very useful in modern programming whenever a process has multiple
tasks to perform independently of the others.
This is particularly true when one of the tasks may block, and it is desired to allow
the other tasks to proceed without blocking
Types Of Threads:
User Threads : User Level Thread is a type of thread that is not created using system
calls. The kernel has no work in the management of user-level threads. User-level threads
can be easily implemented by the user. In case when user-level threads are single-handed
processes user threads are faster to create and manage. If a user thread performs a system call,
which blocks it, all the other threads in that process one also automatically blocked, whole
process is blocked
Advantages
Thread switching does not require Kernel mode privileges.
User level thread can run on any operating system.
Scheduling can be application specific in the user level thread.
• User level threads are fast to create and manage
Disadvantages
Advantages
Kernel can simultaneously schedule multiple threads
from the same process on multiple processes.
If one thread in a process is blocked, the Kernel can schedule another thread
of the same process.
Kernel routines themselves can multithreaded.
Disadvantages
Kernel threads are generally slower to create and manage than the user
threads.
Transfer of control from one thread to another within same
process requires a mode switch to the Kernel.
Single threaded processes -
Single threaded processes contain the execution of instructions in a single sequence. In other
words, one command is processes at a time.
Multithreading -
A process is divided into number of smaller tasks each task is
called a Thread. Number ofThreads with in a Process execute at
a time is called Multithreading. For example, in a browser,
multiple tabs can be different threads. MS Word uses multiple
threads one thread to format the text, another thread to process
inputs, etc.
If a program, is multithreaded, even when some portion of it is
blocked, the whole program is not blocked. The rest of the
program continues working If multiple CPUs are available.
Multithreading gives best performance. If we have only a
single thread, number of CPU’s available, no performance
benefits achieved.
Process creation is heavy-weight while thread creation is
light-weight
Kernels are generally multithreaded
• CODE- Contains instruction
• DATA- holds global variable
• FILES- opening and closing files
• REGISTER- contain information about
CPU state
• STACK-parameters,localvariables,
functions
Benefits
There are four major categories of benefits to multi-threading:
1. Responsiveness - One thread may provide rapid response while other
threads are blocked or slowed down doing intensive calculations.
2. Resource sharing - By default threads share common code, data, and other
resources, which allows multiple tasks to be performed simultaneously in a
single address space.
3. Economy - Creating and managing threads ( and context switches between
them ) is much faster than performing the same tasks for processes.
4. Scalability, i.e. Utilization of multiprocessor architectures - A single
threaded process can only run on one CPU, no matter how many may be
available, whereas the execution of a multi-threaded application may be split
amongst available processors.
Multithreading Models
Many to many relationship.
Many to one relationship.
One to one relationship.
Many-To-One Model
In the many-to-one model, many user-level threads are all mapped onto a single
kernel thread.
Thread management is handled by the thread library in user space, which is very
efficient.
However, if a blocking system call is made, then the entire process blocks,
even if the other user threads would otherwise be able to continue.
Because a single kernel thread can operate only on a single CPU, the many-to-one
model does not allow individual processes to be split across multiple CPUs.
Green threads for Solaris and GNU Portable Threads implement the many-to-one
model in the past, but few systems continue to do so today.
Many-to-one model
One-To-One Model
The one-to-one model creates a separate kernel thread to handle each user thread.
One-to-one model overcomes the problems listed above involving blocking
system calls and the splitting of processes across multiple CPUs.
However, the overhead of managing the one-to-one model is more significant,
involving more overhead and slowing down the system.
Most implementations of this model place a limit on how many threads can be
created.
Linux and Windows from 95 to XP implement the one-to-one model for threads.
One-to-one model
Many-To-Many Model
The many-to-many model multiplexes any number of user threads onto an equal or
smaller number of kernel threads, combining the best features of the one-to-one
and many-to-one models.
Users have no restrictions on the number of threads created.
Blocking kernel system calls do not block the entire process.
Processes can be split across multiple processors.
Individual processes may be allocated variable numbers of kernel threads,
depending on the number of CPUs present and other factors
.
Process Scheduling –
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.
The process scheduling is the activity of the process manager that handles the removal
of the running process from the CPU and the selection of another process on the basis
of a particular strategy.
Process scheduling is an essential part of a Multiprogramming operating system.
Such operating systems allow more than one process to be loaded into the executable
memory at a time and loaded process shares the CPU using time multiplexing
Scheduling Queues
As processes enter the system, they are put into a job queue, which consists of all
processes in the system.
The processes that are residing in main memory and are ready and waiting to execute
are kept on a list called the ready queue. This queue is generally stored as a linked
list. A ready-queue header contains pointers to the first and final PCBs in the list.
Each PCB includes a pointer field that points to the next PCB in the ready queue.
The list of processes waiting for a particular I/O device is called a device queue.
Each device has its own device queue.
This figure shows the queuing diagram of process scheduling.
Queue is represented by rectangular box.
The circles represent the resources that serve the queues.
The arrows indicate the process flow in the system.
Queueing-diagram representation of process scheduling
Schedulers
In a batch system, more processes are submitted than can be executed immediately.
These processes are spooled to a mass-storage device (typically a disk), where they
are kept for later execution.
The long-term scheduler, or job scheduler, selects processes from this pool and
loads them into memory for execution.
The short-term scheduler, or CPU scheduler, selects from among the processes
that are ready to execute and allocates the CPU to one of them.
The primary distinction between these two schedulers lies in frequency of execution.
The short-term scheduler must select a new process for the CPU frequently. A
process may execute for only a few milliseconds before waiting for an I/O request.
Often, the short-term scheduler executes at least once every 100 milliseconds.
Because of the short time between executions, the short-term scheduler must be fast.
If it takes 10 milliseconds to decide to execute a process for 100 milliseconds, then
10/ (100 + 10) = 9 percent of the CPU is being used (wasted) simply for scheduling
the work.
The long-term scheduler executes much less frequently; minutes may separate the
creation of one new process and the next. The long-term scheduler controls the
degree of multiprogramming (the number of processes in memory).
Most processes can be described as either I/O bound or CPU bound. An I/O-bound
process is one that spends more of its time doing I/O than it spends doing
computations.
A CPU-bound process, in contrast, generates I/O requests infrequently, using more
of its time doing computations. It is important that the long-term scheduler select a
good process mix of I/O-bound and CPU-bound processes. The system with the best
performance will thus have a combination of CPU- bound and I/O-bound processes.
Some systems also employ a medium-term scheduler. Medium term scheduling is
part of the swapping. It removes the processes from the memory. It reduces the
degree of multiprogramming. The medium term scheduler is in-charge of handling
the swapped out-processes.
When system loads get high, this scheduler will swap one or more processes out of
the ready queue system for a few seconds, in order to allow smaller faster jobs to
finish up quickly and clear the system.
context switch -
A context switch is the mechanism to store and restore the state or context of a CPU in
Process Control block so that a process execution can be resumed from the same point at
a later time.
Using this technique a context switcher enables multiple processes to share a single CPU.
Context switching is an essential part of a multitasking operating system features.
When the scheduler switches the CPU from executing one process to execute another, the
context switcher saves the content of all processor registers for the process being removed
from the CPU, in its process descriptor. The context of a process is represented in the process
control block of a process.
Context switch time is pure overhead. Context switching can significantly affect
performance as modern computers have a lot of general and status registers to be saved.
Content switching times are highly dependent on hardware support. Context switch requires
( n + m ) bxK time units to save the state of the processor with n general registers, assuming
b are the store operations are required to save n and m registers of two process control blocks
and each store instruction requires K time units.
Some hardware systems employ two or more sets of processor registers to reduce the
amount of context switching time. When the process is switched, the following information
is stored.
Program Counter
Scheduling Information
Base and limit register value
Currently used register
Changed State
I/O State
Accounting
CPU and I/O Burst Cycle: Process execution consists of a cycle of CPU execution and I/O
wait.
Processes alternate between these two states.
Process execution begins with a CPU burst, followed by an I/O burst, then another CPU burst
... etc.
The last CPU burst will end with a system request to terminate execution rather than with
another I/O burst.
The duration of these CPU burst has been measured.
An I/O-bound program would typically have many short CPU bursts, A CPU-bound program
might have a few very long CPU bursts.
This can help to select an appropriate CPU-scheduling algorithm.
Preemptive Scheduling:
Preemptive scheduling is used when a process switches from running state to ready state or
from waiting state to ready state.
The resources (mainly CPU cycles) are allocated to the process for the limited amount of
time and then is taken away, and the process is again placed back in the ready queue if that
process still has CPU burst time remaining.
That process stays in ready queue till it gets next chance to execute.
Non-Preemptive Scheduling:
Non-preemptive Scheduling is used when a process terminates, or a process switches from
running to waiting state.
In this scheduling, once the resources (CPU cycles) is allocated to a process, the process
holds the CPU till it gets terminated or it reaches a waiting state
. In case of non-preemptive scheduling does not interrupt a process running CPU in middle
of the execution.
Instead, it waits till the process complete its CPU burst time and then it can allocate the CPU
to another process.
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
• Arrival Time: Time at which the process arrives in the ready queue.
• Completion Time: Time at which process completes its execution.
• Turnaround time - Time required for a particular process to complete, from
submission time to completion
o Turn Around Time = Completion Time – Arrival Time
• Burst Time: Time required by a process for CPU execution.
• Waiting Time(W.T): Time Difference between turnaround time and burst time.
o Waiting Time = Turn Around Time – Burst Time
• Response time - The time taken in an interactive program from the issuance of a
command to the commence of a response to that command.
The process which arrives first in the ready queue is firstly assigned the CPU.
In case of a tie, process with smaller process id is executed first.
It is always non-preemptive in nature.
Jobs are executed on first come, first serve basis.
It is a non-preemptive scheduling algorithm.
Easy to understand and implement.
Its implementation is based on FIFO queue.
Poor in performance as average wait time is high.
Criteria-Arrival time
Mode-Non-Preemptive
TURN
ARRIVAL BURST WAITING
SS COMPLETION TIME AROUND
TIME TIME TIME
TIME
P0 0 10 10 10 0
P1 1 6 16 15 9
P2 3 2 18 15 13
P3 5 4 22 17 13
o First, the process with the lowest arrival time is scheduled. If two or more of them have the
lowest arrival time, they are scheduled according to their priority.
o Then, other processes will be scheduled according to their arrival time and priority. If two
processes have the same priority they are sorted according to their process number.
o When a process is still in execution and few processes arrive, then they are scheduled in
the ready queue according to their priority.
o This continues till all processes are scheduled.
o P1 has the lowest arrival time so it is scheduled first.
o Next process P4 arrives at time=2. Since P1 has not yet completed its execution,
it waits in the ready queue.
o Then at time=5, process P2 arrives. As P1 is in execution, it goes to the ready
queue. In the queue, there is P4 whose priority is 1 which is less than the priority
of P2. So, P2 is moved to the head of the queue.
o At time=9, P5 arrives. P1 is in execution and in the ready queue we have - P2,
P4, P5.
Priority of P5 is 4 which is less than the priorities of both P2 and P4. So, it is enqueued in the
ready queue at last. The ready queue now becomes - P2, P4, P5
o At time=11, P1 completes its execution. From the ready queue, P2 starts its
execution.
o At time=12, P3 arrives. It has priority 3 which is greater than P5 and less than
P4. So, it gets enqueued before P5. Ready queue now looks like -
P4, P3, P5
o
At time=39, i.e. (11+28 where 28 is the burst time of P2 and 11 is its start time)
P2 completes its execution.
o Then, P4 starts execution at time=39 and completes at time=49.
o At time=49, P3 starts execution which gets completed at time=51.
o At time=51, P5 executes and gets completed at time=67.
o Since, no new process arrives and there is no process remaining in the ready
queue, the scheduling is done.
o Waiting Time Calculation -
P1 11-0-11 = 0
P2 39-5-28=6
P3 51-12-2=37
P4 49-2-10=37
P5 67-9-16=42
o Average Waiting Time = (0+6+37+37+42)/5 = 24.4
As per the Preemptive priority scheduling, the processes will be executed as follows:
Gant Chart
Explanation
There is only P1 available at time 0, so it will be executed first irrespective of the priority
until some other process with a higher priority is encountered by the OS.
At the beginning of the 1st-time unit, we have P2 which has a higher priority than P1, so it
replaces P1.
At 2nd time unit, we have the process P3, but its priority is less than the executing process P2,
so P2 keeps executing without any replacement.
At 3rd time unit, our P2 has been completed and till now we have processes P1 and P3 left.
So, P1 which has a higher priority than P3 is executed.
At 4th time unit, we have process P4 which has the highest priority of them all. So, it
replaces P1.
Since now we have no more processes left to arrive, the processes will now run according to
their priorities and will complete their execution.
• It is designed especially for time sharing systems. Here CPU switches between the
processes. When the time quantum expired, the CPU switched to another job. A small
unit of time, called a time quantum or time slice. A time quantum is generally from 10
to 100 ms. The time quantum is generally depending on OS. Here ready queue is a
circular queue. CPU scheduler picks the first process from ready queue, sets timer to
interrupt after one time quantum and dispatches the process
• Round Robin is the Preemptive process scheduling algorithm.
• Each process is provided a fix time to execute, it is called a quantum.
• Once a process is executed for a given time period, it is preempted and other process
executes for a given time period.
• Context switching is used to save states of pre-empted processes.
PROCESS BURST TIME
P1 30
P2 6
P3 8
Thread Scheduling
To run on a CPU, user-level threads must ultimately be mapped to an associated kernel-level
thread, although this mapping may be indirect and may use a lightweight process (LWP).
Contention Scope
On systems implementing the many-to-one and many-to-many models, the thread library
schedules user- level threads to run on an available LWP. This scheme is known as
processcontention scope (PCS), since competition for the CPU takes place among threads
belonging to the same process.
(When we say the thread library schedules user threads onto available LWPs, we do not
mean that the threads are actually running on a CPU. That would require the operating
system to schedule the kernel thread onto a physical CPU.) To decide which kernel-level
thread to schedule onto a CPU, the kernel uses system-contention scope (SCS).
Competition for the CPU with SCS scheduling takes place among all threads in the system.
Systems using the one-to-one model, such as Windows, Linux, and Solaris, schedule threads
using only SCS. Typically, PCS is done according to priority—the scheduler selects the
runnable thread with the highest priority to run. User-level thread priorities are set by the
programmer and are not adjusted by the thread library.
Pthread Scheduling
Pthreads identifies the following contention scope values:
• PTHREAD SCOPE PROCESS schedules threads using PCS scheduling.
• PTHREAD SCOPE SYSTEM schedules threads using SCS scheduling.
On systems implementing the many-to-many model, the PTHREAD SCOPE PROCESS
policy schedules user-level threads onto available LWPs. The number of LWPs is
maintained by the thread library, perhaps using scheduler activations
The PTHREAD SCOPE SYSTEM scheduling policy will create and bind an LWP for each
user-level thread on many-to-many systems, effectively mapping threads using the one-to-
one policy. The Pthread IPC provides two functions for getting—and setting—the
contention scope policy:
• pthread attr setscope(pthread attr t *attr, int scope)
• pthread attr getscope(pthread attr t *attr, int *scope)
The first parameter for both functions contains a pointer to the attribute set for the thread.
The second parameter for the thread attr setscope() function is passed either the PTHREAD
SCOPE SYSTEM or the PTHREAD SCOPE PROCESS value, indicating how the
contention scope is to be set. In the case of pthread attr getscope(), this second parameter
contains a pointer to an int value that is set to the current value of the contention scope. If
an error occurs, each of these functions returns a nonzero value.
Multiple-Processor Scheduling –
If multiple CPUs are available, load sharing becomes possible—but scheduling problems
become correspondingly more complex.
A second approach uses symmetric multiprocessing (SMP), where each processor is self-
scheduling. All processes may be in a common ready queue, or each processor may have its
own private queue of ready processes.
If we have multiple processors trying to access and update a common data structure, the
scheduler must be programmed carefully. We must ensure that two separate processors do
not choose to schedule the same process and that processes are not lost from the queue.
Virtually all modern operating systems support SMP, including Windows, Linux, and Mac
OS X.
Processor Affinity
Consider what happens to cache memory when a process has been running on a specific
processor. The data most recently accessed by the process populate the cache for the
processor. As a result, successive memory accesses by the process are often satisfied in
cache memory. Now consider what happens if the process migrates to another processor.
The contents of cache memory must be invalidated for the first processor, and the cache for
the second processor must be repopulated.
Because of the high cost of invalidating and repopulating caches, most SMP systems try to
avoid migration of processes from one processor to another and instead attempt to keep a
process running on the same processor. This is known as processor affinity—that is, a
process has an affinity for the processor on which it is currently running.
Processor affinity takes several forms. When an operating system has a policy of attempting
to keep a process running on the same processor—but not guaranteeing that it will do so—
we have a situation known as soft affinity. Here, the operating system will attempt to keep
a process on a single processor, but it is possible for a process to migrate between processors.
Some systems provide system calls that support hard affinity, thereby allowing a process
to specify a subset of processors on which it may run. Many systems provide both soft and
hard affinity. For example, Linux implements soft affinity, but it also provides the
sched_setaffinity() system call, which supports hard affinity.
The main-memory architecture of a system can affect processor affinity issues. Figure
illustrates an architecture featuring non-uniform memory access (NUMA), in which a CPU
has faster access to some parts of main memory than to other parts.
Load Balancing
Load balancing attempts to keep the workload evenly distributed across all processors in
an SMP system. It is important to note that load balancing is typically necessary only on
systems where each processor has its own private queue of eligible processes to execute.
On systems with a common run queue, load balancing is often unnecessary, because once a
processor becomes idle, it immediately extracts a runnable process from the common run
queue.
There are two general approaches to load balancing: push migration and pull migration.
With push migration, a specific task periodically checks the load on each processor and—if
it finds an imbalance— evenly distributes the load by moving (or pushing) processes from
overloaded to idle or less-busy processors. Pull migration occurs when an idle processor
pulls a waiting task from a busy processor.
Multicore Processors
A recent practice in computer hardware has been to place multiple processor cores on the
same physical chip,
resulting in a multicore processor. Each core maintains its architectural state and thus
appears to the operating system to be a separate physical processor. SMP systems that use
multicore processors are faster and consume less power than systems in which each
processor has its own physical chip.
When a processor accesses memory, it spends a significant amount of time waiting for the
data to become available. This situation, known as a memory stall, may occur for various
reasons, such as a cache miss (access ing data that are not in cache memory).
Figure illustrates a dual-threaded processor core on which the execution of thread 0 and the
execution of thread 1 are interleaved.
Thus, on a dual-threaded, dual-core system, four logical processors are presented to the
operating system. The UltraSPARC T3 CPU has sixteen cores per chip and eight hardware
threads per core. From the perspective of the operating system, there appear to be 128
logical processors.
In general, there are two ways to multithread a processing core: coarse grained and fine-
grained multithreading. With coarse-grained multithreading, a thread executes on a
processor until a long-latency event such as a memory stall occurs. Fine-grained (or
interleaved) multithreading switches between threads at a much finer level of granularity—
typically at the boundary of an instruction cycle.
Minimizing Latency
Events may arise either in software —as when a timer expires—or in hardware—as when a
remote- controlled vehicle detects that it is approaching an obstruction. When an event
occurs, the system must respond to and service it as quickly as possible. We refer to event
latency as the amount of time that elapses from when an event occurs to when it is serviced
(Figure).
Interrupt latency refers to the period of time from the arrival of an interrupt at the CPU to
the start of the routine that services the interrupt.
One important factor contributing to interrupt latency is the amount of time interrupts may
be disabled while kernel data structures are being updated. Real-time operating systems
require that interrupts be disabled for only very short periods of time.
The amount of time required for the scheduling dispatcher to stop one process and start
another is known as
dispatch latency.