Operating System
Operating System
Operating System
Process 1 Process 2
X++ Y--
sleep(1) sleep(1)
shared = X shared = Y
Explanation:
We are assuming the final value of a common variable(shared) after execution of Process P1 and Process P2
is 10 (as Process P1 increment variable (shared=10) by 1 and Process P2 decrement variable (shared=11) by
1 and finally it becomes shared=10). If the order of execution of the process (first P1 -> then P2) then we
will get the value of common variable (shared) =9. If the order of execution of the process (first P2 -> then
P1) then we will get the final value of common variable (shared) =11. Here the (value1 = 9) and (value2=10)
are racing, if we execute these two processes in our computer system then sometime, we will get 9 and
sometime we will get 10 as the final value of a common variable(shared). This phenomenon is called race
condition.
Critical Section Problem
Critical Problem – A critical section is a code segment that can be accessed by only one process at a time.
The critical section contains shared variables that need to be synchronized to maintain the consistency of
data variables.
Critical Section Problem – when a process executes the code that manipulates the shared data, (or
recourses), we say that the process is in critical section problem.
Entry section – only one process is allowed to execute in its critical section (even with multiple processors).
So, each process must first request permission to enter its critical section. The section of code implementing
this request is called the entry section
Remainder section – The remaining code is the remainder section.
Solutions:
There are three kinds of solution to critical section problem:
1. Software based solutions
2. Hardware based solutions
3. Operating system based solution
Criteria for a solution:
A solution to the critical section problem must satisfy the following three requirements:
Mutual Exclusion: If process Pi is executing in its critical section, then no other process can be executing in
their critical section.
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 section can participate in the
decision on which will enter its critical section next.
Bounded Waiting There exists a bound 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.
2-Process Solutions to the Critical Section Problem:
This is solution we will be using two process Pi and Pj.
Algorithm 1:
The first approach is to let the processes share a common integer variable turn initialized to 0 or 1. If turn =
= i, then process Pi is allowed to execute in its critical section. Following is the code:
do {
while (turn! =j);
critical section
turn=j;
remainder section}
while (1)
However, it does not satisfy the progress requirement, since it requires strict alternation of processes in the
execution of the critical section.
Algorithm 2:
In algorithm two, the variable turn is replaced with an array Boolean flag [2] whose elements are initialized
too false. If flag is true for a process that indicates that the process is ready to enter its critical section.
do {
flag[i]=true;
while(flag[j]);
critical section
flag[i]=false;
remainder section
}
while (1)
In this solution, the mutual exclusion requirement is satisfied. Unfortunately, the progress condition is not
met; consider the following execution sequence: T0: P0 sets flag [0] = true T1: P1 sets flag [1] = true Now
both the processes are looping forever in their respective while statements
Algorithm 3:
The processes share two variables: Boolean flag [2]; int turn; The Boolean array of ‘flag’ is initialized to
false, whereas ‘turn’ maybe 0 or 1.
do {
flag[i]=true;
turn=j;
while(flag[j] && turn==j);
critical section
flag[i]=false;
remainder section
} while (1)
While the algorithm aims to achieve mutual exclusion, bounded waiting, and progress, it relies on
assumptions that may not hold in all practical systems, particularly regarding the atomicity of operations and
the behaviour of modern processors and memory systems.
The Bakery Algorithm:
(Prerequisite – Critical Section, Process Synchronization, Inter Process Communication)
The Bakery algorithm is one of the simplest known solutions to the mutual exclusion problem for the
general case of N process. Bakery Algorithm is a critical section solution for N processes. The algorithm
preserves the first come first serve property.
Working:
In the Bakery Algorithm, each process is assigned a number (a ticket) in a lexicographical order. Before
entering the critical section, a process receives a ticket number, and the process with the smallest ticket
number enters the critical section. If two processes receive the same ticket number, the process with the
lower process ID is given priority.
Notation – lexicographical order (ticket #, process id #) – Firstly the ticket number is compared. If same
then the process ID is compared next. The numbering scheme always generates numbers in increasing order
of enumeration; i.e., 1, 2, 3, 3, 3, 3, 4, 5, …
Fairness:
The Bakery Algorithm ensures fairness by assigning a unique ticket number to each process based on a
lexicographical order. This ensures that processes are served in the order they arrive, which guarantees that
all processes will eventually enter the critical section.
if i < j
Pi is served first;
else
Pj is served first.
Advantages of Bakery Algorithm:
Fairness
Easy to implement
No deadlock
No starvation
Disadvatnges:
Not scalable
High time complexity
Busy waiting
Memory Overhead
Hardware based solution for critical section problem:
Process Synchronization problems occur when two processes running concurrently share the same data or
same variable. The value of that variable may not be updated correctly before its being used by a second
process. Such a condition is known as Race Around Condition. There is a software as well as hardware
solutions to this problem. Hardware instructions in many operating systems help in the effective solution of
critical section problems.
There are three algorithms in the hardware approach of solving Process Synchronization problem:
1. Test and Set
2. Swap
3. Unlock and Lock
Test and Set:
There is a shared variable called lock that starts as false. The first process checks the lock. It finds the lock is
false and sets it to true. This process enters the critical section (a special part of the code). While the first
process is in the critical section. Any other process that checks the lock finds it true. These processes have to
wait. When the first process leaves the critical section. It sets the lock back to false. Now, any waiting
process can check the lock. The first one to find it false sets it to true and enters the critical section.
Pseudo code:
boolean lock;
Boolean TestAndSet (boolean &target) {
boolean rv = target;
target = true;
return rv;
}
while (1){
while (TestAndSet(lock));
critical section
lock = false;
remainder section
}
Swap:
Swap algorithm is a lot like the TestAndSet algorithm. Instead of directly setting lock to true in the swap
function, key is set to true and then swapped with lock.
The first process sets its key to true and swaps it with lock. Initially, lock is false, so after the swap. lock
becomes true (because key was true). The process's key becomes false (because lock was false). The process
checks its key, finds it false, so it breaks out of the while loop and enters the critical section. Any other
process sets its key to true and swaps it with lock. Since lock is now true (set by the first process), after the
swap. lock remains true (since the new process's key was true). The process's key becomes true (because
lock was true). The process checks its key, finds it true, and stays in the while loop, waiting. When the first
process leaves the critical section, it sets lock back to false. Any waiting process sets its key to true again
and swaps it with lock. Now, lock is false (since the first process reset it), so after the swap. lock becomes
true (because the new process's key was true). The process's key becomes false (because lock was false). The
process checks its key, finds it false, and enters the critical section.
Only one process in the critical section at a time. If the critical section is empty, a process can enter. Not
guaranteed. There's no order, so some processes might wait longer.
Pseudo Code:
boolean lock;
Individual key;
void swap (boolean &a, boolean &b) {
boolean temp = a;
a = b;
b = temp;
}
while (1) {
key = true;
while(key)
swap(lock,key);
critical section
lock = false;
remainder section
}
Unlock and Lock:
Uses Test and Set but adds a waiting array for each process and maintains a queue to manage the order of
processes. Uses TestAndSet to check and set lock. It also checks the waiting array and the queue to see if
other processes are waiting. Added to the queue if they can't enter the critical section immediately. the queue
and sets waiting for the next process to false, allowing it to enter. If no processes are waiting, it sets lock to
false.
Only one process in the critical section at a time. If the critical section is empty, a process can enter.
Guaranteed. Processes are handled in a queue, ensuring each gets a turn.
Pseudo code:
boolean lock;
Individual key;
Individual waiting[i];
while(1){
waiting[i] = true;
key = true;
while(waiting[i] && key)
key = TestAndSet(lock);
waiting[i] = false;
critical section
j = (i+1) % n;
while(j != i && !waiting[j])
j = (j+1) % n;
if(j == i)
lock = false;
else
waiting[j] = false;
remainder section
}
Semaphore:
Semaphore is an integer variable which is used in mutual exclusive manner by various concurrent
cooperative process in order to achieve synchronization. A semaphore is a synchronization tool used to
control access to a shared resource in concurrent programming. It is an integer variable that can only be
changed using two atomic operations: wait (P) and signal (V).
Types of semaphore:
There are two kinds of semaphore:
Binary semaphore – whose integer value cannot be > 1; can be simpler to implement.
Counting semaphore – whose integer value can range over an unrestricted integer domain.
Basic Operation:
Wait (P): This operation decreases the semaphore value by 1 if the semaphore value is positive. If the value
is 0 or negative, the process waits.
Pseudo code:
wait(S) {
while (S <= 0) ;
// no operation (busy wait)
S--;
}
Signal (V):
This operation increases the semaphore value by 1, possibly allowing a waiting process to proceed.
Pseudo code:
signal(S)
{
S++;
}
Mutex:
For multiple processes to safely share a resource, they use a semaphore called mutex (short for mutual
exclusion). The semaphore mutex is initialized to 1. Each process performs the following steps:
Wait on mutex: This ensures that only one process can enter the critical section at a time.
Critical Section: The part of the code where the shared resource is accessed.
Signal mutex: This allows other processes to enter the critical section.
Pseudo code:
Do
{
wait(mutex);
// Critical section
signal(mutex);
// Remainder section
}
while (1);
Spinlock:
During implementing the mutex, if a process can't enter the critical section, it keeps checking (busy waiting).
This wastes CPU cycles, especially in multiprogramming systems where many processes share a single
CPU. This type of semaphore is also called a spinlock. Because the process spins while waiting for the lock.
Improving Semaphores to Avoid Busy Waiting:
To avoid busy waiting, semaphores can be modified to make processes block themselves when they can't
proceed.
Working:
Blocking: The process suspends itself and is added to the waiting queue.
Wakeup: A blocked process is moved to the ready queue when another process signals the semaphore.
Wait Operation:
Decrease the semaphore value.
If the value is negative, add the process to a waiting queue and block it.
Pseudo code:
void wait(semaphore S) {
S.value--;
if (S.value < 0) {
add this process to S.L;
block(); }
}
Signal Operation:
Increase the semaphore value.
If the value is still negative, remove a process from the waiting queue and wake it up.
Pseudo code:
void signal (semaphore S) {
S.value++;
if (S.value <= 0) {
remove a process P from S.L;
wakeup(P);
}
}
Semaphore Structure
A semaphore can be represented as follows:
typedef struct {
int value;
struct process *L; // list of waiting processes
} semaphore;
value: The current value of the semaphore.
L: A list of processes waiting on the semaphore.
Disadvantages of semaphore:
While semaphores help with mutual exclusion and progress, issues like deadlock, starvation, and violation
of mutual exclusion can still occur.
Deadlocks
A deadlock occurs when a set of processes are waiting for each other to release resources, causing them to
be stuck forever.
Starvation
Starvation occurs when a process waits indefinitely because other processes keep getting priority for the
required resources.
Violation of Mutual Exclusion
If semaphores are misused, mutual exclusion can be violated, allowing multiple processes to enter the
critical section simultaneously.
Classical Problems of synchronization:
There are three classical problems of synchronization:
Bounded buffer problem
Reader and writers’ problem
Dining Philosophers problem
Bounded buffer problem:
The bounded-buffer problem involves a fixed-size buffer that producers and consumers share. Producers add
items to the buffer, and consumers remove items from it.
Semaphores Used:
mutex: Ensures mutual exclusion when accessing the buffer (initialized to 1).
empty: Counts the number of empty slots in the buffer (initialized to the buffer size, n).
full: Counts the number of full slots in the buffer (initialized to 0).
Reader and writers’ problem:
This problem involves a shared data object that can be read by multiple readers simultaneously, but only
written by one writer at a time.
Semaphores Used:
mutex: Ensures mutual exclusion when updating the count of readers (initialized to 1).
wrt: Ensures mutual exclusion for writers or a writer and readers (initialized to 1).
readcount: Keeps track of the number of readers currently accessing the data (initialized to 0).
Dining Philosophers problem:
In this problem, five philosophers sit at a table with five chopsticks. Philosophers need two chopsticks to eat
and release them after eating. The challenge is to prevent deadlock and starvation.
Semaphores Used:
chopstick [5]: Represents the chopsticks (each initialized to 1).
Solution:
This simple solution can lead to deadlock if all philosophers pick up their left chopstick simultaneously.
Solutions to prevent deadlock include:
Allowing only four philosophers to sit at the table.
Ensuring a philosopher picks up both chopsticks only if both are available.
Using an asymmetric solution where odd philosophers pick up the left chopstick first, and even
philosophers pick up the right chopstick first.
Disadvantages:
Swapping involves transferring data between memory and the backing store, which can be time-consuming.
Care must be taken to handle situations like pending I/O operations that could conflict with swapped-out
processes.
c)Contiguous Memory Allocation (MFT Multiprogramming with fixed tasks):
Memory is divided into fixed partitions, with each process requiring a single contiguous block of memory.
Working:
Processes are loaded into partitions of predetermined sizes. Each process gets a base and limit register pair
to define its memory boundaries.
Example:
BM's OS/MFT (Multiprogramming with Fixed Tasks) allocates fixed partitions for processes. Once a
partition is free, a new process can be loaded into it.
Advantages and Disadvantages:
Advantage: Simple to implement and understand.
Disadvantage: Can lead to internal fragmentation (wasted space within partitions) and lacks
flexibility for dynamically changing memory needs.
d) Multiprogramming with Variable Tasks (MVT):
An evolution of MFT where memory partitions are variable in size, adapting to the needs of different
processes.
Working:
Processes can occupy memory regions of varying sizes, reducing internal fragmentation compared to MFT.
Example:
Instead of fixed partitions, MVT adjusts memory allocations dynamically, accommodating processes of
different sizes more efficiently.
Disadvantages:
MVT eliminates internal fragmentation but can lead to external fragmentation (unused memory scattered in
small chunks). Compaction (shuffling processes in memory) can reduce external fragmentation but at the
cost of slower performance.
e) Paging:
Paging is a way for computers to manage memory that allows a program's memory to be stored in non-
contiguous blocks. Instead of keeping all parts of a program together in memory, paging divides memory
into small fixed-sized blocks called "pages". These pages are typically 4 KB or 8 KB each.
Why use paging?
Avoids Fragmentation: In traditional memory management, fitting different-sized chunks of
memory (like programs or data) into available spaces can leave unused gaps (fragmentation). Paging
helps avoid this issue.
Efficient Use of Memory: By breaking memory into fixed-sized pages, the computer can allocate
and manage memory more efficiently.
Protection: Each process gets its own page table, which maps virtual addresses (used by programs)
to physical addresses (actual locations in memory). This separation helps protect one program's
memory from being accessed by another.
Working:
Pages and Frames: Physical memory is divided into fixed-sized blocks called frames, and logical memory
(how programs see memory) is divided into pages of the same size (process is divided into same size pages).
Page Table: Each process has a page table that keeps track of which pages of its memory are stored in which
frames of physical memory.
Address Translation: When a program accesses memory, the CPU translates its virtual address (from the
program) into a physical address (where data is actually stored). This translation is done using the page
table.
Advantages:
Simplicity: Simplifies memory management by treating memory as small fixed-sized units.
Flexibility: Allows different programs to use memory efficiently without worrying about the
physical location of each piece of data.
Security: Ensures one program cannot access another's memory directly, improving system security.
Example:
Imagine a book where each page is a fixed size (like 4 KB). Instead of putting the entire book in one place
on a shelf (contiguous memory), you store each page wherever there's space. A table tells you which page is
where, so you can find any page quickly when needed.
Implementation of Page Table in Different Places:
Page Table in CPU Registers
Suitable for small processes and large page sizes.
Advantage: The time to access memory (effective memory access time) is nearly the same as just accessing
the memory directly. This is fast.
Example: The PDP-11 computer.
2. Page Table in Main Memory
Uses a special register called the Page Table Base Register (PTBR) to point to the page table in main
memory.
Disadvantage: The effective memory access time becomes double the normal memory access time,
which is too slow for efficient program execution.
3. Page Table in Translation Look-Aside Buffer (TLB)
TLB: A small, fast cache that holds a few entries from the page table.
Entries: Each entry is a key-value pair (page number, frame number).
When the CPU generates a logical address (page number, offset):
o TLB Hit: If the page number is found in the TLB, the corresponding frame number is
quickly retrieved to form the physical address.
o TLB Miss: If the page number is not found in the TLB, the page table in main memory is
checked, and the TLB is updated with this new (page number, frame number) pair.
Context Switch: When the CPU switches between different processes, the TLB is cleared (flushed)
and reloaded with entries relevant to the new process.
Paging Hardware with TLB
Hit: Accessing the memory involves the time to check the TLB and then the memory.
Miss: Checking the TLB, then accessing the memory twice (once to get the frame number from the
page table, and once to access the actual data).
Performance of Paging
Effective Memory Access Time: How fast memory can be accessed on average, considering hits
and misses in the TLB.
o Hit Ratio (HR): The percentage of times the needed page number is found in the TLB.
o Miss Ratio (MR): The percentage of times the needed page number is not found in the TLB
(MR = 1 - HR).
Formula: