Operating Systems 2
Operating Systems 2
CSC3002F
Processes
• Program: static piece of compiled code
o By itself, does nothing
o Only becomes process when run by OS
• Process: program in execution
• OS provides environment for program execution and services to those executing programs
• OS distinguishes between two different kinds of processes:
o OS processes – run at more privileged level, provides services that OS exposes to
other kind of process
o User based process – the kind of process that we run
▪ This process may need to access kernel services which are themselves
processes
• OS has (OS processes + user processes) executing concurrently
• Have large number of processes running in system, all doing work, in various stages
o Need to ensure that they’re executing correctly
o Want to avoid inappropriately access shared memory etc
• Three main phases for processes:
o (1) Creation – initialisation, what code they run, etc
o (2) Scheduling – which process should run next
▪ Needed because have limited # of processing cores
▪ OS creates illusion of infinite # of processers – and all processes can run
concurrently (impossible)
• This happens when have worst case of one core
▪ OS scheduler deals with all this
o (3) Termination
• OS also enables inter-process communication (ipc)
• Process: unit of work in a time-sharing/multi-tasking OS
o Also called a job
o All modern OSes are time-sharing/multitasking OSes – appear to be running multiple
tasks at the same time
▪ Need to use time-sharing mechanism to create this illusion
• A user creates a process by double-clicking or running a program from the command line
o Two copies of a program running are two separate processes – OS is smart enough
to not duplicate program code, and other shared states between them
• A program is a static entity (the code)
o The specification, compiled code that process has to run
• A process is a dynamic entity – a program in memory executing or ready to execute
o Need to consider memory implications i.to usage
Process vs Program
• OS provides this process to run program – has lower access lvl than kernel
o Kernel is core of OS – provides services to user programs
o Distinguish between things running in kernel vs user mode
2|Page
▪ User can only run processes in user mode – lower access processes which
can’t modify certain things e.g. an area of disk/memory
• Such services would be provided by kernel through a kernel API
• User process would use that to get access to protected services
• A process is the execution of an application program with restricted rights
o The process is the abstraction for protected execution provided by the operating
system kernel
o Kernel provides restricted environment for program to run in
• A process needs permission from the operating system kernel before accessing the memory
of any other process, before reading or writing to the disk, before changing hardware
settings, and so forth
• Linux – use ps -A cmd
o Many options for ps
o PID is process ID – unique
o Child process can be spawned by parent process – thus have PID
o Processes can also be execution environment for other code
Process Tree
• Parent process creates children processes, which, in turn create other processes, forming a
tree of processes
o Every process has a parent – exception is process created by kernel when the PC first
boots
▪ In diagram, given name init
• Identified and managed via a process identifier (PID)
3|Page
o Started by the kernel itself, so in principle it does not have a parent process
o Always has process ID of 1
• Web browsers such as Chrome, Internet Explorer, Firefox, and Safari, play a role similar to an
OS
• Browsers load and display web pages, but many pages embed scripting programs that the
browser must execute
o These scripts can be buggy or malicious; hackers have used them to take over vast
numbers of home computers
Process Creation
Process Context
4|Page
• A process is a program together with its execution context which encompasses:
o The state of the processor (processor context), which is the value of its program
counter and all of its registers
▪ Recall that program counter is where next instruction is stored in memory
▪ This part of execution context is necessary for a process to be stopped and
then restarted at a later point
o Process’ memory map, which identifies the various regions of memory that have
been allocated to the process
▪ Can involve paging, etc
• A process in memory comprises:
o Text (compiled code) – static program; has no meaning unless executing (then
considered a process)
o Current state (incl. program counter and registers)
o Have memory regions that process stores info as it executes
▪ Static region contains global data – fixed over process’ lifetime
• Corresponds to static keyword in languages
o Stack (function parameters, return addresses, temporary data/local data)
▪ The stack holds the state of local variables during procedure calls.
▪ Limited resource – stores function call mechanism in memory
▪ When call function – essentially push series of parameters onto stack that
represent arguments to that function and return address (so function can
return to correct place and continue executing code)
▪ Function can call another function – creates another stack frame, which
does the same
▪ When create local variable inside a function – that variable is created on
stack as well
▪ Stack can run out with deep recursion
▪ On diagram – arrows pointing towards each other should never converge
• If they do, have crit failure of memory resource and will have some
kind of error e.g. segmentation fault / other kernel lvl signal to show
there’s an issue with memory usage
o Data (initialized static and global variables)
o Heap (memory dynamically added during runtime – e.g. using malloc or new)
▪ The heap holds any dynamically allocated data structures the program might
need
▪ Dynamic store – e.g. all calls to new in Java, c++ ex require space from heap
▪ Used, then freed up and returned to the system
▪ Happens automatically in java
Process Lists/Table
• To keep track of processes, the operating system maintains a process table (or list)
o List/ double linked list data structure
• Each entry pertains to each process which is running/ can run in that system
o Process isn’t always running – can be sleeping, waiting for i/o
o Need to record these states so OS can operate appropriately e.g schedule next
available thread/process onto the next core
5|Page
• Large, complete data strc – stores all info that kernel requires to manage processes
effectively
o Scheduler makes heavy use of PCB
• Each entry in the process table is a Process Control Block (PCB)
o Corresponds to a particular process
o Contains fields with information that the kernel needs to know about the process
• A process is identified by a unique process ID (PID)
• Some OSes (UNIX-derived systems) have a notion of a process group
o A way to group processes together so that related processes can be signalled
▪ E.g. send terminal signal to process group – all processes in that group will
be terminated
o Collection of processes are doing some common task
o Every process is a member of some process group
6|Page
▪ For well-managed processes – need to place limits on them (can’t hog core)
o I/O status information – I/O devices allocated to process, list of open files
▪ Series of queues attached to each IO device
▪ Info that pertains to files that the process is manipulating
• Aside – modern OSes operate at finer granularity and use threads (essentially lightweight
processes)
Process Representation
• Linux:
o In Linux, the PCB is implemented as the data structure task_struct
▪ Contain info about a process, such as process id, process state, file
information, and so on
• OSes are written in C – lower lvl language
o C is well-suited to resource management at a low lvl
• The process block is the most complicated data structure in the operating system
o E.g. task_struct contains 83 field variables; 60 are primitive types, 3 are composite
data structures, and 20 are pointers to composite data structures
7|Page
Process Creation Unix
Process Creation
• When creating a child process – need to determine how resources will be allocated to that
child
• Resource sharing options for processes
o Parent and children share all resources
o Children share subset of parent’s resources (e.g. Linux)
o Parent and child share no resources (e.g. Windows)
• Also have different options for how child and parent will execute after the fork() cmd – i.e.
child process creation
• Execution options
o Parent and children execute concurrently – as two independent processes that can
terminate separately or parent can wait for child
8|Page
▪ Also can overlay an additional program in child process and have it execute
that instead – child executes its own program
o Parent waits until children terminate
• Address space
o Child duplicate of parent - behaviour of fork system call in UNIX
▪ Have the same window in the address space – allows for easy sharing of info
between processes
▪ But often also want child to do something else
o Child has a program loaded into it - behaviour of exec system call in UNIX
▪ Replaces parent program with a new program – creates a new address space
and destroys whatever was there before and now operate completely
independently
• Running different programs
▪ A common use case
UNIX Example
9|Page
Fork Example
• Fork is always used to create a child process from another process – can cascade these
• Execution continues IN BOTH process, after the fork() command
o Running the same code in parallel (unless exec used)
• Can check process ID
o If zero, we know we’re in a child process with some parent that used fork() to create
it
• Q: How may processes will be created by the small program below?
• A: 8!
o Doubling each time we fork() – why?
o At first lvl – program executes, hits first fork cmd, creates a new child process
▪ Now have parent and child
o One fork cmd has occurred – so crossed out
o Execution resumes in both processes after that
▪ Another fork – both go ahead and execute
▪ Now have two separate processes, executing fork
o Each of these now create another process
▪ Parent spawns a new child – 3rd column
▪ Another fork crossed out
▪ Now have parent and child, and then original child and its child
o All four of these have fork cmd to execute
▪ That happens – creates new subchild for each of those
• Now have 8 processes running
10 | P a g e
Process State
11 | P a g e
• Process can be moved off core by an interrupt – most common interrupt type is when
process exceeds its execution quantum
o To give illusion of large array of processes running concurrently, need to time-slice
the amount of time that they can be run
o Only allow process to run from start to finish if it’s short – otherwise after a certain
amount of time, we interrupt its running on the core
Context Switching
• Entire execution environment – all the state it needs to run
• When a process is running, the system is said to be in the context of that process.
• Context switching can also be used for interrupts and system calls
• When the kernel decides to execute another process, it performs a context switch, causing
the system to execute in a different process’ context
o For a context switch, the kernel has to save enough information so that it can switch
back to the earlier process and continue executing exactly where it left off
o When a process executes a system call and has to change from user to kernel
mode, the kernel also has to save enough information so that it can later return to
user mode and continue executing the program
▪ System calls run in kernel mode because they have a privileged level –
they’re accessing low lvl resources
▪ Existing state has to be saved while system call is managed
▪ Need to restore process to its state prior to save occurring
• 75,000-100,000 context switches per second not uncommon!
o Context switching needs to happen very fast
• Diagram shows process that has been interrupted – another process will be switched in by
scheduler
o Need to keep cores occupied at all times otherwise sys degrades
• Context switch steps:
o Save current process to PCB – save current state
o Decide which process to run next
12 | P a g e
oLoad of new process from PCB – load pcb of new process that has been swapped in,
and continue executing
• Context switch should be fast, because it is overhead- the system does no useful work
while switching processes
• Diagram – process P0 on lhs, busy executing
o Sold line indicates where process is doing nothing because a sys call has happened
o E.g. i/o operation
o Save pcb for process – takes an amount of time
▪ Other process P1 is idle – thus its wasted time
▪ Want to minimise this time – context switch needs to be fast
o Reload state of process that want to swop in – P1
▪ Executes and will service interrupt / manage sys call
▪ Once complete, gives up control of call that its been running on
▪ Loads/saves its state so that it can be resumed later on if necessary
o Reload original process P0
• Have a lot of idle time – want to minimise this
• The more complex the OS and the PCB ➔ the longer the context switch
o Speed at which it happens depends on OS complexity, peculiarities of pcb (how info
rich it is)
• Time taken is dependent on hardware support (helps manage context switches):
o Hw designers try to support routine context -switch actions like saving/restoring all
CPU registers by one pair of machine instructions
o Some hw provides multiple sets of registers per CPU ➔ multiple contexts loaded at
once
13 | P a g e
Queue Examples
Process Scheduling
14 | P a g e
o Exits to the right when terminating
o Or can experience a number of different conditions – moves it off the CPU and onto
a different queue
• IO request comes in – process migrates (because of that request) onto io wait queue
o Sits on queue until io resource available – once it is, and can service io
request/transaction, process moves onto ready queue
• Timeslice expires, process has to move off CPU so something else can run
o Moves directly back into ready queue
• Process could also create child – has to wait until child terminates (depending on nature of
parent-child relationship)
o Goes onto child-termination wait queue
o Once child terminates, can move onto ready queue again and be rescheduled
• If interrupt occurs when process running (last condition)
o Moves into interrupt wait queue
o Once interrupt is serviced, moves onto ready queue
Scheduler
• Short-term scheduler (or CPU scheduler) – selects process to be executed next and allocates
CPU
o Sometimes the only scheduler in a system
o Invoked frequently (milliseconds) – must be fast (cores needs to have something
running on it at all times)
o Needs to made decisions rapidly – algos used to make these decisions need to be
simple and sophisticated enough that they can be executed quickly
• Long-term scheduler (or job scheduler) – selects processes to be brought into the ready
queue
o Invoked infrequently (seconds, minutes) – may be slow
o Controls the degree of multitasking – i.e. the number of concurrent processes in
memory (can run, but aren’t necessarily running at a particular point in time)
o Brings in processes with lower priority – these processes often aren’t in main
memory
▪ Moves them into memory – now available for short term scheduler
• Processes can be described as either:
o I/O-bound – spend more time doing I/O than computations, many short CPU bursts
▪ Slow, ends up waiting a lot
▪ Long-term scheduler concerned with this
o CPU-bound – spends more time doing computations; few very long CPU bursts
• Problem is striking a balance between how much CPU time these processes get – where
the scheduler comes in
o Long-term scheduler strives for good process mix – collection of CPU-bound
processes doesn’t hog all the computational cores (otherwise io won’t happen)
o But also, don’t want io processes lingering on CPU – they’ll just need to be swoped
off again
▪ IO-bound thus need a fair chance at CPU, but must not be unnecessarily
swopped in – will need to do a context swop
15 | P a g e
Aside: Multitasking in Mobile Systems
16 | P a g e
Inter-process Communication
17 | P a g e
IPC Models: Message Passing
• Message system – processes communicate with each other without resorting to shared
variables, using mechanisms for processes to communicate and to synchronize their
actions
o Have send and receive msg facility which all processes can access
o One process will send msg – arrives in msg queue
o Receiving process will basically receive a msg from a queue (i.e. pull a msg off)
• IPC facility provides two operations:
o send(message)
o receive(message)
o Msg size fixed or variable
• Examples:
o Windows advanced local procedure call (ALPC) facility
▪ Allows methods on a local system to behave akin to RPCs
o Pipes (named or unnamed) – UNIX (also Windows)
▪ Ordinary pipes – io device that child and parent use to communicate
(without using shared memory)
▪ Create a child with fork, then create pipe which allows parent and child to
write info back and forth
▪ Named pipes – persistent data structures that exist on disk
• Processes can read to/write from – requires more setup
o Client-server communication
▪ Sockets
▪ Remote Procedure Calls (RPCs) – have processes on different machine that
communicate with each other (using RPCS, pass msgs)
Process Termination
• Process terminates when it executes last statement
o Once process has finished all its work – can either put explicit call to exit, or use
return
▪ Generates call to exit with appropriate return code
o Asks the OS to delete it using the exit() system call
• On exit, process may return a status value (typically an integer) to a waiting parent process
o Parents wait for children with a wait() system call – moves onto ready queue until
that call is serviced
• On exit, all process resources are deallocated and reclaimed by the OS
o Including physical and virtual memory, open files, and I/O buffer, pcb and process ID
• Termination can occur in other circumstances
• A process can terminate another process via a system call
18 | P a g e
o e.g TerminateProcess() in Windows, abort() in Unix/Linux (abort exits without doing
proper resource management)
• Usually, a terminate system call can be invoked only by a parent process
o Otherwise, a user, or a misbehaving app could arbitrarily kill another user’s
processes
o Because a parent needs to know the identities of its children to terminate them, the
identity of the newly created process is passed to the parent on creation
• A parent may terminate the children processes because:
o Child has exceeded allocated resources
o Task assigned to child is no longer required
o The parent is exiting and the OS does not allow a child to continue if its parent
terminates
• Once a child process exits and lets OS know by issuing its exit cmd – OS recoups all resources
for that process
o However, at that point it hasn’t completely recovered the pcb – so there’s info in the
pcb that lingers until parent calls wait, or otherwise acks that process has completed
• After exiting, a child process still has an entry in a process list, so waiting parent can check
that is it complete and read its status code
o If no parent waiting (did not yet invoke wait()) process is a zombie – resources
freed up but entry still in pcb (parent needs to query that status)
▪ Once parent calls wait, process unravels
o If parent terminated without invoking wait(), process is an orphan – pcb is still
active, process lingers on system
▪ On UNIX systems, orphaned processes are generally inherited by process
with id 1, which then proceeds to kill them
o Parents need to check status of child process – wait() return child’s status (i.e. what
was the termination code for the child)
▪ That info is stored in pcb for the process – all other info can be recovered,
but some needs to stay there so that the correct behaviour occurs when
wait cmd is serviced
• Once process exits and resources recovered, parent issues wait cmd and allows it to be
serviced, and then returns status code to parent
o Parent can then go on and issue its own exit and terminate
19 | P a g e
Multithreading
Threads
• Most modern applications have multiple “threads of control”, or threads, in order to process
more than one task at a time
• Thread is a basic unit of CPU utilization
• Thread comprises thread id, program counter, register set and a stack
• Threads within a process share code, data and files + signals etc, with creating parent
process
o Other things are unique to each thread e.g. registers, stack
• Can only have one thread running on any one time on a core – hence why multiple
cores/CPUS is advantageous
o With multiple cores, can have multiple threads running concurrently
On Processors
20 | P a g e
• It is the job of the OS to provide the illusion of a nearly infinite number of virtual processors
o Achieved by timeslicing, moving threads off the cores, etc
Multithreading Benefits
• Responsiveness – may allow continued execution if part of process is blocked (e.g. an io
operation gets serviced), especially important for user interfaces and servers
o If only have one thread of execution, for io, entire process halts
o With multiple threads, these “subprograms” can continue running on different cores
while io request is serviced
• Resource sharing – threads share resources of process, easier than shared memory or
message passing
• Economy – cheaper than process creation, thread switching has a lower overhead than
context switching
o Process is a heavyweight thread – need to create a process to act as container for
the threads
• Scalability – process can take advantage of multiprocessor/ multicore architectures: more
concurrency
Kernel threads
• The OS kernel is multithreaded
• In Unix/Linux ps –ef will display the kernel threads
• The kernel thread kthreadd (pid = 2) is the parent of all other kernel threads
21 | P a g e
▪ This is what is scheduled by scheduler to the various cores
▪ Have escalated level of privilege
o Need mapping between user and kernel threads
• Mapping between user and kernel threads can be:
o Many-to-One – all user threads (created by a user level thread library) are mapped
onto a single thread
▪ Create illusion that system is multithreaded
▪ Pure user-level thread implementation
▪ If one thread blocks, all block
▪ Few systems currently use this model, because multiple threads do not run
in parallel on multicore systems
• No collection of threads running in the kernel, so can’t have true
parallel support for threads (thus can’t run concurrently)
o One-to-One – every user thread is mapped onto corresponding kernel thread and is
dispatched onto different cores
▪ Creating a user-level thread creates a kernel thread
▪ # threads per process typically restricted
▪ Users can create a large number of threads – have to decide how to
schedule those
• OS can impose constraints as to how many threads one can create to
ensure system doesn’t get overwhelmed
o Many-to-Many – take large collection of user threads and map them to a smaller
collection of kernel threads
▪ E.g. Windows, Linux
▪ Many user-level threads to be mapped to as smaller or equal number of
kernel threads
▪ Allows the OS to create a sufficient number of kernel threads
▪ Mapping chosen to optimise various criteria around resource utilisation
• OS tries to ensure that it services as many threads as possible using
resource optimally
• E.g. threading waiting on io will be swooped out
22 | P a g e
Thread Libraries
• A thread library provides programmer with API for creating and managing threads
• May be:
o Entirely in user space, no kernel support OR
▪ E.g. many-to-one, don’t use kernel directly at all – create and manage
threads as lightweight user processes
• Mapping needs to then happen
o Kernel-level library supported by the OS
▪ Directly support creation of kernel threads – different levels of privilege
• Three primary thread libraries:
o POSIX Pthreads – user-level, kernel-level
o Windows threads – kernel lvl
▪ Operate with elevated access
o Java threads – JWM uses thread library on host system,
▪ e.g. Windows threads on Windows and Pthreads on Unix/Linux
23 | P a g e
Pthreads
• A POSIX standard (IEEE 1003.1c) API for thread creation and synchronization
o May be provided either as user-level or kernel level
o Specification, not implementation
▪ API specifies behaviour of the thread library, implementation is up to
development of the library
• Common in UNIX operating systems
• Diagram:
o Top function is method run by thread
▪ Takes variable as an arg, and increments it
▪ Passing reference to variable – change persists to parent code
o Begin in main process (thread of execution)
▪ Print out their values
o Create reference to new thread
▪ Then in pthread_create, thread creation happens
o Thread now active
o Main program does the same operation – increments y
o Thread rejoins with parent before terminating
24 | P a g e
Java Threads
• Managed by the JVM, specification does not say how threads are mapped onto kernel
threads
o Provides consistent API for the user to create threads
o Typically implemented using the threads model provided by underlying OS
o Two techniques for creating threads in a Java program:
▪ (1) Create a new class that is derived from the Thread class and to override
its run() method
▪ (2) Define a class that implements the Runnable interface
o When a class implements Runnable, it must define a run() method
▪ The code implementing the run() method then runs as a separate thread
Implicit Threading
• In this model, creation and management of threads is done by compiler and threading
library, to take burden off programmer
o Rather than directly creating threads, create tasks
o These tasks are taken by a framework, mapped onto threads and then distributed to
different cores
• E.g. Java fork join, OpenMP (high lvl programming construct)
o OpenMP is embedded in C, c++ - use hash pragmas (instructions to pre-processor,
compiler to parallelise the code given sum constraints)
25 | P a g e
Threading Issues
Semantics
• Semantics of fork() and exec() system calls – fork() system call is used to create a separate,
duplicate process
o Does fork()duplicate only the calling thread (thread that fork was issued in), or all
threads (in the process)?
▪ Some UNIX systems have two versions of fork which operates at two
different levels
▪ Duplicating all is resource intensive
▪ When issued in process, fork() duplicates entire process – those processes
then carry on executing
o exec() usually works as normal – replace the running process including all threads
▪ Takes new process and overlays a new memory space and some executable
to run in
Signal Handling
• Signals are used in UNIX systems to notify a process that a particular event has occurred
o Process has to respond
o E.g. kill signal to terminate process
• May be synchronous (e.g. division by 0) or asynchronous (e.g. terminating a process or
expiration of a timer)
o Synchronous need to be operated on immediately/asap – managed by thread which
triggered that signal
▪ E.g. thread has division by zero or illegal memory access
▪ Thread receives signal and must act on it
26 | P a g e
oAsynchronous – emerge from outside a thread
▪ Instruction to terminate a process, an interrupt to stop a particular thread
from executing (so another can be swopped in)
• For single-threaded, signal delivered to process
• Where should a signal be delivered for a multi-threaded program?
o Deliver the signal to the thread to which the signal applies
o Deliver the signal to every thread in the process
o Deliver the signal to certain threads in the process
o Assign a specific thread to receive all signals for the process (and execute specific
responses to that signal)
• The choice of which paradigm to use depends on program and what support is available in
the OS
• Most versions of Unix allow threads to specify which signals it will accept and which not
o However, if there’s a particular signal they can all respond to, signals delivered only
to first accepting thread only
▪ Signal then considered to be processed
Thread Cancellation
• Terminating a thread before it has finished – thread to be cancelled is target thread
• Two general approaches:
o Asynchronous cancellation terminates the target thread immediately
o Deferred cancellation allows the target thread to periodically check if it should be
cancelled, allows the thread to terminate in an orderly fashion
• Deferred cancellation is generally preferred to asynchronous termination
• Asynchronous cancellation is problematic where resources allocated to a cancelled thread
are either not released or are in an unsafe state
27 | P a g e
Synchronisation
• Sync – ensuring that certain set of relationships hold over a sequence of events
o Want to ensure, by using the correct sync primitives, that the correct order of
events always plays out and ordering of events is guaranteed and consistent
• Here, synchronization refers to relationships among events—
o any number of events and
o any kind of relationship (before, during, after)
• Synchronization constraints are requirements pertaining to the order of events
• In OSes, often need to satisfy synchronization constraints without the benefit of a clock,
either because there is no universal clock, or because we do not know to a fine enough
resolution when events occur
• Sync primitives don’t depend on time
• Signalling – requirement that if have multiple threads, one particular statement executes
in one thread before another
▪ E.g. Thread A, statement a1 must execute before statement b1, thread B
28 | P a g e
• Rendezvous – requirement is that a certain order of execution is maintained for
statements in different threads
o E.g. Thread A, statement a1 executes before statement b2, thread B AND that b1,
thread B must execute before statement a2, thread A
29 | P a g e
Synchronisation Mechanisms
• Solutions to sync problems
o Techniques range from hardware to software-based API’s
• All based on protecting critical regions through use of locks
o Two processes cannot have a lock simultaneously
30 | P a g e
o Mutual Exclusion – if process Pi is in its critical section, no other processes can be in
their critical sections
o Progress – if no process is executing in its critical section and some processes wish
to enter their critical sections, then only those processes that are not executing in
their remainder sections can participate in deciding which will enter its critical
section next, and this selection cannot be postponed indefinitely
▪ Basically, any process that wishes to enter its crit section should be allowed
to compete and should be allowed to make that decision in a reasonable
amount of time
o Bounded Waiting – there exists a bound, or limit, on the number of times that other
processes are allowed to enter their critical sections after a process has made a
request to enter its critical section and before that request is granted
▪ Assume that each process executes at a nonzero speed
▪ No assumption concerning relative speed of the n processes
▪ Once process requests access to crit section – should receive access within a
reasonable amount of time
31 | P a g e
test-and-set()
o test-and-set on LHS
o RHS shows critical section is executed – once finished, the lock is released
compare-and-swap()
o Lock initially set to 0/false – if value matches expected value, can grab the lock
32 | P a g e
▪ Lock assumes values 1 in that case
▪ Return original value of lock, which is 0
o Once lock grabbed, can execute crit section
▪ Any other process trying to grab lock will fail
o Once crit section completed, set lock to zero – allows other program waiting to
enter its critical section
• Note: these approaches do not satisfy “bounded waiting” – a solution in terms of CAS is
provided in textbook
o Guarantees at most n-1 waiting turns for n threads
Atomic Variables
• The compare-and-swap() hardware not usually used directly to provide mutual exclusion
o Used to construct synchronization primitives such as atomic variables
• Updates to atomic variables are guaranteed to be atomic – no data races on a single variable
• E.g. might have variable x and want to atomically increment/decrement it some languages
have direct support for this through hw
o Guarantee with atomic increment on variable, that nothing can interrupt it
• Operation sequence for increment involves several registers – those can be interrupted by
interleaving of instructions from different processes
o Making an operation atomic allows a variable to be safely incremented without
having to worry about operations being interleaved or inconsistent data (due to
data races)
• Diagram shows example of atomic increment
• Hardware solutions are complicated and generally inaccessible to application programmers,
so OS designers build software tools to solve critical section problem
• Simplest is the mutex lock, which has an associated Boolean variable “available” to
indicate if the lock is available or not
o Two operations to access - acquire() and release()
33 | P a g e
• Calls to acquire() and release() are atomic- usually implemented via hardware atomic
instructions
o Can’t be interrupted
• Solution requires busy waiting – lock is therefore called a spinlock
o Wasteful – CPU core runs tight while loop, constantly checking the state of this
variable
▪ Not doing any productive work
o Busy waiting seen as bad (from performance pov) – BUT if crit sections are short,
then amount of time spent in crit section will be small
▪ Thus, amount of time spent waiting for lock to be freed up is small
o With multiple CPUs with multiple cores, could have process spinning on spinlock on
one core, other cores occupied by other process
o But issues can occur when only have single core
• Diagram – while in crit section, available is false
o Once done, set available to true
o These operations are atomic
Semaphores
34 | P a g e
▪ Once original process signals that its finished, only one other process is then
able to grab the semaphore, decrement the value and enter its crit section
(blocking other processes)
• When you create the semaphore, you can initialize its value to any integer, but after that the
only operations you are allowed to perform are value test, increment and decrement
o Decrement – a thread can only decrement the semaphore if it is > 0
▪ Otherwise the thread blocks itself and cannot continue until another thread
increments the semaphore
o Increment – when a thread increments the semaphore, if there are other threads
waiting, one of the waiting threads gets unblocked
• In the original definition you cannot read the current value of the semaphore
o BUT Java will let you do this
Types of Semaphores
• Binary semaphore – integer value can range only between 0 and 1
o Same as a mutex lock
o Generally initialised to 1
• Counting semaphore – integer value can range over an unrestricted domain
o Used to control access to a restricted number of processes
o Some value n > 1
o Place resource limit on the # of processes that can simultaneously enter their critical
section
o Multiplex
Example
• If the value of the semaphore is positive, then it represents the number of threads that can
decrement without blocking
• If the value is zero, it means there are no threads waiting, but if a thread tries to decrement,
it will block
• NOTE – if it is negative (permitted under the non-busy-waiting implementation) then the
value represents the number of threads that have blocked and are waiting
Semaphore Implementation
• Must guarantee that no two processes can execute the wait() and signal() on the same
semaphore at the same time (atomic) – usually done with hardware locks
35 | P a g e
• Also, want to avoid busy waiting - although this is not a problem if critical section, and hence
waiting time, is short, also busy waiting avoids a context switch
o Modify wait() so that process blocks (suspends) and is put in waiting queue
▪ CPU scheduler chooses another process to execute
o Each semaphore now has two data items:
▪ Semaphore value (of type integer)
▪ List of processes in waiting queue
• Requires two additional operations:
o sleep()
▪ Require by wait() – suspend invoking process (the processes must also be
added to waiting queue)
o wakeup(P)
▪ Required by signal() – remove a process P from the waiting queue, call
wakeup(P)
• Scheduler can then dispatch it again (it is now ‘runnable’)
• Signalling:
o Thread B – have semaphore initialised e.g. to 0
o Statement a1 has to execute first since semaphore can only be passed once it’s
value is >1, so its value can be decremented down to zero
▪ Can only happen once signal() increments sem to 1
o Once incremented, b1 can execute
• Rendezvous:
36 | P a g e
o aArrived and bArrived initialised to 0
o a1 and b1 has no constraints – can execute in interleaved fashion
o a1 executes, then b1 (or other way around)
o Then one of these will run its signal first on a particular semaphore
o E.g. aArrived does it first – semaphore goes up to 1
▪ Then tries to execute wait – has to wait because value of bArrived is still 0
▪ Assume bArrived executes at some point – semaphore value increases to
one
▪ bArrived wait() can execute now
▪ Then a2 will be executed
▪ Similarly, correct sequence of operations ensure that thread B’s statements
will execute correctly
Monitors
• Alternative to semaphores
• High-level abstraction that provides a convenient and effective mechanism for process
synchronization
o Using semaphores is prone to error – can put wait() and signal() in incorrect place or
swop them around, refer to wrong semaphore with wait() or signal() etc
▪ Leads to hard-to-detect semaphore bugs
o Can alternatively wrap sync behaviour in abstract data type – monitor
37 | P a g e
• Abstract data type – internal variables only accessible by code within the procedure
• Monitor is collection of data and series of methods that are guaranteed exclusive access to
that data
o Guarantee that only one of those methods can access those methods at any one
time
o Java has monitor data type
• Only one process may be active within the monitor at a time
• But not powerful enough to model some synchronization schemes
o Need some way to signal between different methods that some condition has
changed and now another method is allowed to start operating on monitor
o Achieved via condition variables
Condition Variables
• Have condition x
• Two operations are allowed on a condition variable:
o (1) x.wait()
▪ A process that invokes the operation is suspended until x.signal()
o (2) x.signal()
▪ Resumes one of processes (if any) that invoked x.wait()
▪ If nothing waiting on the variable, then it has no effect on the variable
38 | P a g e
39 | P a g e
Classic Problems of
Synchronisation
Producer-Consumer Problem
• In multithreaded programs there is often a division of labour between threads
• In one common pattern, some threads are producers and some are consumers
o Producers create items of some kind and add them to a data structure;
o Consumers remove the items and process them
• E.g. Event-driven programs
o Whenever an event occurs, a producer thread creates an event object and adds it to
the event buffer
o Concurrently, consumer threads (“event handlers”) take events out of the buffer
and process them
▪ Event handlers
• Constraints to be enforced by synchronization:
o While an item is being added to or removed from the buffer, the buffer is in an
inconsistent state. Therefore, threads must have exclusive access to the buffer
o If a consumer thread arrives while the buffer is empty, it blocks until a producer
adds a new item
o In bounded buffer variant: producer cannot produce if buffer of BUFFER_SIZE is full
Pseudocode
40 | P a g e
Producer-Consumer
• Pseudocode shows there’s nothing to provide mutex access to parts where buffer needs to
be updated, as well as updating the counter
o These parts need to be protected to prevent inappropriate access which would lead
to data inconsistencies
With Semaphores
• Shared variables:
o int n ;
▪ Size of the buffer
o Semaphore mutex = 1 – binary semaphore
▪ Ensures mutex access to crit section
o Semaphore full = 0
▪ Counting semaphore
▪ Provides the # fully occupied buffer slots that consumer can pull data from
o Semaphore empty = n
▪ Counting semaphores
▪ Provides # of empty slots for producer to write into
Semaphores Pseudocode
• Only one process can have mutex at a time – once mutex grabbed, either p or c has it
o Can then do updates
• Producer waits(empty)
o Number of slots available for it to write on
o Can immediately pass through because all buffer slots are available
o Waits until it can grab mutex, once it has
▪ It updates, puts item in buffer
o Then signals an increase in full
• Consumer waits(full) i.e. there is something there for it to consume
o If item in the buffer, it can then pass by wait and decrement full to 0
o Grabs mutex to do its update
o Signals that there’s one more slot available to write info into
41 | P a g e
• Producer now has more space to insert things in
Diagram
• Producer waits until buffer isn’t full – tries to get mutex lock
o Once it grabs it, it can safely add to buffer and do its updates
o Can then release the mutex
Reader-Writers Problem
42 | P a g e
• Any situation where a data structure, database, or file system is read and modified by
concurrent threads, some of which read and others write
o Can’t be reading when records are being modified
o When writing into, no other thread should be writing at the same time
• As with Producer-Consumer, asymmetric solution (different code for readers and writers)
• Synchronization constraints:
o Any number of readers can be in the critical section simultaneously
▪ Use counting semaphore
o Writers must have exclusive access to the critical section
• “Categorical mutual exclusion” – have two different categories of thread and we treat them
differently
• Several variants of how readers and writers are considered – all involve priority
o First variation – no reader kept waiting unless writer has permission to use shared
object
▪ Favours readers – unless writer in crit section, should get readers in asap
▪ Once in, all readers can pile in – writer has to wait until all readers are
finished
o Second variation – once writer is ready, it performs the write ASAP
43 | P a g e
• Not the best solution
Dining Philosophers
44 | P a g e
With Semaphores
• Have semaphores – call wait until we get the left and right chopstick
o Then eat – once done, call signal() to release system resources
▪ Put down left then right chopstick
o Mod operation is because the table is circular (last philosopher grabs the first
chopstick) – using circular buffer
Deadlock
45 | P a g e
▪ Even-numbered philosopher picks up first the right chopstick and then the
left chopstick
▪ Preferred solution
o Also issue of starvation…
46 | P a g e
Deadlock
System Model
• Represents OS
• System comprises:
o A finite number of resources
▪ CPU cycles, files, and I/O devices
▪ Also sync tools such as mutex locks and semaphores
o Distributed among a number of competing threads
▪ Want threads to have fair access to these resources
• Under this simplified system model, a thread may use a resource in only the following
sequence:
o Request – if request can’t be immediately honoured, thread must wait
o Use – once acquired, often use exclusively
o Release – so that other threads competing for resource can access it
• A set of threads is in deadlock when every thread in the set is waiting for an event that can
be caused only by another thread in the set
o OS system kernel – have many threads that are potentially “scheduleable” and are
competing for resources
• Sync tools are the most common sources of deadlock
Safety vs Liveness
• There are two types of correctness properties for concurrent programs:
o Safety properties – the property must always be true
▪ E.g. acquiring a semaphore to get mutex access to crit section; using
mutexs/semaphores to ensure specific order of operation
o Liveness properties – the property must eventually become true
▪ Weaker constraint
▪ Liveness failure – most exemplified by deadlock
▪ Livelock – have set of processes unable to make progress at all
47 | P a g e
• Spin around doing no useful work
• E.g. acquire and release lock – doing something, but it’s not
productive
• Solutions to safety issues often raise the possibility of liveness issues
Deadlock Characterisation
• Four necessary conditions must be satisfied simultaneously for deadlock to occur:
o Mutual exclusion- only one process at a time can use a resource
o Hold and wait: a process holding at least one resource is waiting to acquire
additional resources held by other processes
o No pre-emption – a resource can be released only voluntarily by the process holding
it, after that process has completed its task
o Circular wait – there exists a set {P0, P1, …, Pn} of waiting processes such that P0 is
waiting for a resource that is held by P1, P1 is waiting for a resource that is held by
P2, …, Pn–1 is waiting for a resource that is held by Pn, and Pn is waiting for a
resource that is held by P0
48 | P a g e
▪ Edges from resource to process
• Diagram:
o Have P1 requesting resource from resource pool R1, but R1 is allocated to P2
o Similarly, for P2, R3 and P3
o P1 and P2 requesting from R2
o Snapshot of resource request and allocation in a system
• If graph contains no cycles => no deadlock
• If graph contains a cycle:
o If only one instance per resource type, then deadlock exists
o If several instances per resource type, then possibility of deadlock
▪ This is the case with the diagram above
▪ Possible to break cycle and work out process of sequence execution such
that process can finish its task, free up its resources and honour resource
request edges from other processes
Checkpoint
49 | P a g e
• Yes – P1 to R3 to P3 to R1 to P1
o All nodes participating in cycle and only one resource available for each resource
node in that cycle
• (a) Deadlock possible – but have more than one resource per node
o Deadlock potential but not guaranteed
o If P4 executed first, frees up resource to go back to resource pool R2
▪ P3 can then acquire resource – becomes resource assignment edge from R3
to P3
▪ Could then free up resource in R1 and R2 – P4 and P3 now finished, P1 can
grab resource
o Multiple solutions to avoid deadlock
• (b) Multiple cycles – all involve resource nodes with more than one resource element
o Deadlock – no way to satisfy resource requests to break deadlock condition
• If have cycle and resource nodes have single resource, definitely deadlock
50 | P a g e
o But with multiple resource elements per resource node/vertex, then need to look at
problem more carefully and see if there’s a sequence of operations which can be
performed which will allow for resources to be gradually freed up
Handling Deadlock
• Pretend that deadlocks never occur in the system (ignore the problem)
o E.g. most OS’s, including Windows and Linux
o Up to kernel and application developers to write programs that handle deadlocks
• Use a protocol to prevent or avoid deadlocks, ensuring that the system will never enter a
deadlocked state
o Deadlock prevention – ensure that at least one of the necessary conditions cannot
hold
▪ All 4 conditions have to hold for deadlock to occur – if can get around one of
them, then can prevent deadlock
o Deadlock avoidance – OS requires advance information on which resources a thread
will request
▪ Additional info given to system when trying to acquire resources
▪ System runs some algos, does some computations to detect if honouring
that resource request is going to lead to deadlock in future
• Could then deny request, put process on wait queue and try again
later
o Deadlock recovery – allow the system to enter a deadlocked state, detect it, and
recover
▪ E.g. dtbs – commit, rollback if there’s a problem and then rollback to
previous state
51 | P a g e
▪ Can roll-back to prior non-deadlocked state
Deadlock Prevention
• Four necessary conditions must be satisfied simultaneously for deadlock to occur
o Which of these can we remove?
▪ To prevent deadlock from occurring
• (1) Mutual exclusion – only one process at a time can use a resource
o Safety is an absolute requirement – removal cannot be used for prevention
• (2) Hold and wait – a process holding at least one resource is waiting to acquire additional
resources held by other processes
o Have beginnings of a circular dependency – have resource, but trying to grab
another one
▪ In grabbing another, doesn’t necessarily free the one you’re holding
o Could say – can only request a resource, it’s not allowed to hold any other resources
▪ Or it needs to request all resources before it begins execution
▪ Or allow it to request resources only when no other is allocated to it
▪ All of these are impractical, bad solutions with performance hits
o Removal impractical – low resource utilization
o Starvation possible
• (3) No pre-emption – a resource can be released only voluntarily by the process holding it,
after that process has completed its task
o Removal impractical for most systems – performance implication for constantly
interrupting threads in order to release their resources
o Sync primitives used to avoid/control deadlock can’t be interrupted
▪ i.e. atomic operations protecting crit sections
• (4) Circular wait – there exists a set {P0, P1, …, Pn} of waiting processes such that P0 is
waiting for a resource that is held by P1, P1 is waiting for a resource that is held by P2, …,
Pn–1 is waiting for a resource that is held by Pn, and Pn is waiting for a resource that is held
by P0
o Create total ordering of resource types i.e. sequential ordering of resources
o Processes request resources – walk through sequence from start to end, can find a
way of avoiding it
o Use resource graphs
52 | P a g e
Deadlock Avoidance
• Ensure that the system will never enter a deadlock state
o Essentially force all threads in system to declare their resource requests upfront
• Requires additional a priori information available on possible resource requests
o Simplest and most useful model requires that each process declare the maximum
number of resources of each type that it may need
o The deadlock-avoidance algorithm dynamically examines the resource-allocation
state to ensure that there can never be a circular-wait condition
o Resource-allocation state is defined by the number of available and allocated
resources, and the maximum demands of the processes
• System is in safe state if there exists a sequence of ALL the processes in the system, such
that for each Pi the resources that Pi can still request can be satisfied by currently available
resources + resources held by all the Pj, with j < i
o Sequence that is determined must allow us, for any particular entry in the sequence,
must be able to satisfy the requests of that thread using only the free, available
resources at that time AND the resources held at that time by a lower index
o E.g. have 5 threads in system, and we’re working with thread 3 T3
▪ When looking at T3s request to see if it can be honoured, we must look at all
available resources, as well as those held by T1 and T2
o If this property holds for all of the Pi in the sequence that was determined, then
can’t enter circular wait state and thus can’t deadlock
• That is:
o If Pi resource needs cannot be met now, then Pi can wait until all processes Pj ( j < i )
have finished executing
▪ When they have finished executing, they release all their resources and then
Pi can obtain the needed resources, execute, return allocated resources, and
terminate
▪ When Pi terminates, Pi+1 can obtain its required resources, and so on
• When predecessor threads finish – they return allocated resources
to pool and thus allows next process in sequence to grab those and
execute
o Execution may be deferred for a short period BUT still be able to execute –
preferable to deadlock (have no control over resource requests)
53 | P a g e
▪ There are some performance hits – but can workout sequence of thread
execution (such that conditions hold), then can guarantee circular wait can’t
occur
• If a system is in safe state => no deadlocks
o i.e. obeys above properties -
• If a system is in unsafe state => possibility of deadlock
o i.e. can’t find sequence of thread execution (and resource deallocation when thread
terminates)
o Deadlock not guaranteed but possible under certain conditions
Example
54 | P a g e
▪If T1 executes and returns 2 resources, now only have 4 resources in
resource pool
▪ Can’t do anything else – T2 needs 6 and T0 needs 5
• Ensure that a system will never enter an unsafe state
• Whenever a thread requests a resource that is currently available, the system decides
whether the resource can be allocated immediately or the thread must wait
o The request is granted only if the allocation leaves the system in a safe state
o If a thread requests a resource currently available, may have to wait
o Resource utilization may be lower than it would otherwise be
Resource-Allocation-Graph Algorithm
55 | P a g e
• Use a variant of the resource -allocation graph with claim edges
o Claim edge Pi → Rj indicates that process Pj may request resource Rj
▪ Represented by a dashed line
o Claim edge converts to request edge when a process requests a resource
o Request edge converted to an assignment edge when the resource is allocated to
the process
o When a resource is released by a process, assignment edge reconverts to a claim
edge
▪ May want to claim that resource again at a later point in execution
o A cycle in the graph implies that the system is in unsafe state
• Suppose that process Pi requests a resource Rj
o The request can be granted only if converting the request edge to an assignment
edge does not result in the formation of a cycle in the resource allocation graph
o Otherwise, the process must wait
o Check for safety with a cycle-detection algorithm
▪ Order of n^2 operations, where n is the number of threads in the system
▪ Expensive
56 | P a g e
Bankers Algorithm
57 | P a g e
Safety Calculation
58 | P a g e
Example
59 | P a g e
o Only have T4 left – can satisfy that
o 4 3 1 < 10 5 5
o Add 0 0 2 + 10 5 5 = 10 5 7 (all system resources)
• The system is in a safe state as the sequence < T1, T3, T0, T2, T4> satisfies safety criterion
Example
60 | P a g e
o Is request < available resources i.e. 1 0 2 < 3 3 2? Yes!
o Update structures to see if going to be in safe state
▪ Available = available – request i.e, 3 3 2 – 1 0 2 = 2 3 0
▪ Allocation = allocation + request i.e. 2 0 0 + 1 0 2 = 3 0 2
▪ Need = Need – request i.e. 1 2 2 – 1 0 2 = 0 2 0
▪ Run safety algo
▪ The system is in a safe state since the sequence < T1, T3, T4, T0, T2> satisfies
safety criteria.
▪ Request is therefore granted
o Request (3,3,0) by T4
▪ Need T4 is 3 3 0 < 4 3 1 so request < need
▪ BUT 3 3 0 > 2 30 so cannot grant this
o Request (0,2,0) by T0
▪ Need T0 is 0 2 0 < 7 4 3 so request < need
▪ 0 20 < 2 3 0 so request < available resources
▪ So mod vectors
• Available = 2 3 0 – 0 2 0 = 2 1 0
• Allocation = 0 1 0 + 0 2 0 = 0 3 0
• Need = 7 5 3 – 0 3 0 = 7 2 3
▪ Run safety check – result is unsafe state
▪ Restore old resource info – go back to previous request
▪ Force T0 to wait
Deadlock Recovery
• Allow system to enter deadlocked state and then recover
• Requires:
o Deadlock detection
▪ Detection algorithms for
• Single instance of a resource type
• Multiple instances of a resource type
▪ Recovery scheme – so system can recover and continue to do useful work
61 | P a g e
Deadlock detection
Single instance of each resource type
Resource Pre-emption
• Forcibly remove resources from threads that’re deadlocked, in order to break the deadlock
o Again, there are implications
• Selecting a victim – minimize cost
o Choose a thread to “steal resources from” which minimises impact on the rest of the
system
• Rollback – return to some safe state, restart process for that state
o Take process and remove its resources – in doing so, have interfered with its
computation
o Thus, need to backtrack to a point and restart thread where it can continue with
computations and try and reacquire that resource again
o Not a trivial task
• Starvation – same process may always be picked as victim, include number of rollback in cost
factor
o Due to victim selection criteria, could end up picking the same victim – thread will be
unable to acquire any resources
o May need to keep track of # of rollbacks – additional cost
▪ If rollback too many times, then won’t steal resource from it
Transactions
• Transactions are used widely in file systems and databases
o Transaction either complete (and transaction commits) or do not happen at all
(aborts)
• Transactional systems are good for deadlock recovery:
o Thread roll back
63 | P a g e
▪ Roll back or undo threads actions to a clean state (picking one or more
victim threads)
o Thread restarting
▪ Victim thread is restarted
o Would have to be built on top of OS in practice – thus difficult to get working at user
thread level
▪ Would also need to maintain data strcs to maintain this transactional nature
64 | P a g e
CPU Scheduling
65 | P a g e
o This distribution (i.e. proportion of each of these cycles) is important when
implementing a CPU-scheduling algorithm
o Continual interleaving of the CPU and IO burst cycles
• A priori there is no global scheduling algo that works well for all possible scenarios – need to
look at characteristics of your sys, the computation and IO characteristics and choose
scheduler based on that
• Graph above shows a well-balanced system
o CPU bursts are quite short – then have a long tail (small number of long CPU bursts)
CPU Scheduler
• Selects one of the processes in the ready queue to be executed
• Ready queue can be implemented as:
o FIFO queue
o A priority queue
o A tree
o An unordered linked list
o Choose which can based on the complexity of underlying system or how entries
need to be inserted into queue
• The records in the queue are process control blocks (PCBs)
o PCB contains necessary info to execute processes – process state, peripheral data,
etc
• How does the operating system choose which thread to run next?
• CPU scheduling decisions can take place when a process switches from:
o (1) Running state to waiting state
o (2) Running state to ready state
o (3) Waiting state to ready state
o (4) Running state to terminated
• Scheduler decides how to do these transitions and what process to swop in once something
has been swopped out
o Trying to max CPU utilisation – don’t want CPU to be idle
66 | P a g e
o E.g. when going from running state to waiting state – want another thread to be
scheduled in
• Non-pre-emptive/cooperative scheduling if done in situations 1 and 4 only
o Once it has CPU, process keeps it until terminated or waiting
o If only use this kind of scheduling = huge performance hit
▪ Have processes that will hog CPU
• Otherwise pre-emptive scheduling
o Can result in race conditions
• Virtually all modern OS’s (e.g. Windows, Mac OS X, Linux, and UNIX) use pre-emptive
scheduling algorithms
Scheduling Criteria
• Criteria depends on specifics of underlying system
• Have a series of tradeoffs – want to ID way of picking certain metrics and comparing them
against each other for different utilisation patterns in OS
o Use that to try to decide which scheduler makes sense for a particular use case
• Range of different scheduling algorithms with different properties
• Many potential criteria for comparison, such as:
o Maximise CPU utilization – keep the CPU as busy as possible
▪ Looking at the single core case
▪ Obviously, the best utilisation depends on the device e.g. don’t want 90%
utilisation for a mobile device (drains battery too fast)
o Maximise throughput – number of processes that complete their execution per time
unit
o Minimise turnaround time = interval from the time of submission of a process to the
time of completion
▪ i.e. total amount of time a process has been waiting in the ready queue
o Minimise total amount of time a process has been waiting in the ready queue
o Minimise response time = time to start responding
▪ Amount of time it takes from when a request was submitted until the first
process response is produced, not output (for time-sharing environment)
Scheduling Algorithms
• Concerned with the problem of deciding which of the processes in the ready queue is
allocated the CPU’s core
• Focus first on uniprocessor/ single core systems
• Five algos:
o (1) First–come, First-serve (FCFS)
o (2) Shortest-Job-First Scheduling (SJF)
o (3) Round-Robin Scheduling (RR)
o (4) Priority Scheduling
o (5) Multilevel Queue Scheduling
67 | P a g e
o Process at head of queue scheduled next on CPU
• Advantages:
o Simple
o “Fair”
• Disadvantages – average waiting time/ average response time can be long
o E.g. a long job can be scheduled onto CPU and hog it
o Less equitable access to CPU
• Gantt Chart denotates how long process takes to execute and their order
• Series of process arrive at time 0 – each process has a burst time measured in a unit
(milliseconds for examples)
• P1, P2, P3 arrive at the same time – using FIFO scheduler, P1 first placed on CPU and
executes
o Runs for 24 ms – then P2 (goes to 27 ms), and then finally P3 at 30 ms
• Waiting times for processes can be computed and calculate the average
o Avg waiting time = 17 ms – an example of convoy effect
o Convoy effect: short process queued behind long process
▪
o NB this is a possible exam question
68 | P a g e
• With a different ordering, as seen directly above, can see that have an avg waiting time of 3
ms
o Much lower than 17ms from previous example
• Is FIFO sensible? Yes! It can be!
o When all tasks that need to be accomplished will each take roughly the same
amount of time, then it doesn’t matter
• Aside:
Shortest-Job-First Scheduling
• Under this approach, next job to be chosen by scheduler is the one that has the least
remaining time
o Not concerned with length of the running process – only at burst time
• Actually shortest-next-CPU-burst algorithm
• Associate with each process the length of its next CPU burst – when a process arrives at the
queue
o Use these lengths to schedule the process with the shortest time from those
currently in the queue
• SJF is provably optimal for response time:
o Minimum average Waiting time for a given set of processes
o Consider a case where have a short burst-time process scheduled after a long one
▪ If computing avg waiting time as metric – if move the short one before the
long time, then avg waiting time decreases for the entire system
• Example:
o All processes arrive at time 0 – p4 waiting time = 0ms, p3 = 3ms, etc
▪ 7ms as avg waiting time
o FCFS is longer
69 | P a g e
• Accurate implementation is impossible.
• Approximate implementation is difficult:
o Hard to predict the length of the next CPU burst
▪ Thus, have to model – need some prediction scheme to guess at the next
burst time and use that as burst length for incoming job
• Assume that the next CPU burst will be similar to previous
o Predict length of next CPU burst as exponential average of previous – moving avg
which exponentially waits closer samples
▪ Samples closer to current point in time will receive a heavier weight –
further away get a much lower weight
o Pick the process with the shortest predicted CPU burst – schedule on that basis,
apply SJF algo to that
• Formalised:
o α weight between 0-1 – weighting factor that determines relative importance of
contributing terms
▪ α will sum to 1
o t is system burst time – don’t know upfront, use hindsight (deducible once the burst
has happened)
o τ is predicted burst value at a particular time step
o τn+1 – value used in SJF algo at next time step
70 | P a g e
▪ Predicted next CPU burst length is linear weighted sum of the previous
actual burst length + some other weighted term that encapsulates entire
history up until that point
▪ Basically, a recurrence relation
▪ As one iterates, τn will accumulate more info about the past
▪ Need to set τ0 – e.g. a constant
• Example:
• Black line is the actual burst time, blue represents the value computed by exponential
averaging scheme – lags when updated with new values, until the sequence of burst times
become similar (then converges)
• Establish impact of weighting using recurrence relations τn+1 – expand it
o By plugging in values and iteratively expanding them out – get an equation
o Terms very far in past have a high exponential factor (e.g. τ0) – and these factors
are less than one
▪ If take value between 0-1 and multiply it by itself repeatedly, it gets smaller
and smaller quickly
▪ Thus, impact of very distant terms compared to current, is very very low
• Extreme of α value – setting α to 0 will make formula reduce to next predicted burst (ie
entire history), but ignores most recent burst info that you get from system
o Use if system is unstable and burst times are not converging
• If α set to 1 – next predicted burst is the same as previous actual burst
o Ignoring history completely
• So, with guess τj = 10 and cpu burst τi = 6, then 5+3=8 (diagram)
o And so on
o Eventually, guesses being made start converging on the actual burst time – once
system has settled down
71 | P a g e
Pre-emptive or Non-pre-emptive
o P1 arrives and runs for 8ms – but all the other processes arrive during that 8ms
o Once p1 finishes, choose the one with the next shortest burst time – p2, then p3 and
finally p3
o Avg waiting time is thus 7.75ms (worse then pre-emptive but better than FCFS)
Potential Issues
• If have long task, it will be switched on and off the CPU quite frequently, especially if it’s
mixed in with a bunch of smaller tasks
o This requires a context switch each time – adds to the general system overhead
• Potential to have starvation if have a bunch of shorter jobs that pre-empt a longer job
72 | P a g e
o Depending on job length, may never get to run, or very little time – will take much
longer to finish
73 | P a g e
o Short processes have a burst time shorter than the time quantum – when scheduled,
they’ll be able to run to completion
o P1 will run for time quantum, pre-empted (placed on the back of the queue)
▪ Then p2 scheduled – and so on
o When only p1 left – can be scheduled to be exclusively run on the queue or it could
be interrupted every quantum, chosen to run on the queue (and so on)
▪ Depends on scheduler
• Waiting time for round-robin approach isn’t necessarily better than others – because for
longer programs, they have to wait a long time to achieve completion
• Better response time because only need to run a process until it gets to a point where it can
start returning data/producing a response that’s useful for the user
• A bigger quantum size can have unexpected results on turnaround time
• Average Turnaround time of a set of processes does not necessarily improve as the time-
quantum size increases
o Average turnaround time can usually be improved if most processes finish their next
CPU burst in a single time quantum.
74 | P a g e
o A rule of thumb is that 80 percent of the CPU bursts should be shorter than the time
quantum
▪ Can use statistical data and then modify time quantum dynamically
depending on computational load
Priority Scheduling
• A priority number (integer) is associated with each process
• CPU allocated to the process with the highest priority
o Preemptive – higher priority process can pre-empt the CPU and start running
o Non-preemptive
• SJF is actually priority scheduling, where priority is the inverse of predicted next CPU burst
time.
• Usually smallest # ≡ highest priority
• Priorities can be assigned internally – e.g. . I/O burst or externally (importance of process
etc.)
o Problem: Starvation (indefinite blocking) – low priority processes may never
execute.
o Solution: Aging – as time progresses increase the priority of the process
• Can combine priority scheduling and RR
o System executes the highest priority process; processes with the same priority will
be run using round-robin
o
• For priority scheduling, O(n) search may be necessary to determine the highest-priority
process.
o Easier to have separate queues for each distinct priority, priority scheduling
schedules the process in the highest - priority queue.
o A process is then permanently in a given queue.
• Can also partition according to type of process, eg. background /foreground
o i.e. some queues associated to some kinds of processes
o Foreground – need more rapid response time
o Background – run more slowly, and less frequently (lower priority)
• Each queue has its own scheduling algorithm:
o Foreground – RR
o Background – FCFS
75 | P a g e
• Biggest issue: need to be able to extract tasks across all the queues to run on the collection
of cores
o Can service the high priority queues, and then move down – i.e. only service the
next pq when the current one is empty
▪ Problem – those current queues may never empty, leads to starvation
o Could thus allocate a fraction of the CPU budget to each of the queues
• Scheduling must be done between the queues:
o Fixed priority scheduling; (i.e., serve all from foreground then from background).
Possibility of starvation.
o Time slice – each queue gets a certain amount of CPU time which it can schedule
amongst its processes;
▪ i.e., 80% to foreground in RR
▪ 20% to background in FCFS
• A process can move between the various queues – depending on various criteria
o Aging can be implemented this way
o Multilevel-feedback-queue scheduler defined by the following parameters:
▪ Number of queues
▪ Scheduling algorithms for each queue
▪ Method used to determine when to upgrade a process
▪ Method used to determine when to demote a process
▪ Method used to determine which queue a process will enter when that
process needs service
76 | P a g e
o Littles law – in steady state, processes leaving queue must equal processes arriving,
thus:
▪ n=λxW
▪ Valid for any scheduling algorithm and arrival distribution
77 | P a g e