Unit 2 Operating System
Unit 2 Operating System
o Pros: It is the fastest method because data isn't copied; processes read and write
directly.
o Pros: Easier to implement and safer, as the kernel manages the communication.
It is ideal for distributed systems where processes are on different machines.
o Cons: Slower than shared memory due to the overhead of system calls and data
copying.
1. Pipes: Unidirectional data channels (like a water pipe). Data flows from a "write" end to
a "read" end.
1. Anonymous Pipes: Used between related processes (e.g., parent and child).
2. Named Pipes (FIFOs): Can connect unrelated processes and appear as special files on
the disk.
2. Message Queues: A linked list of messages stored in the kernel. Processes can leave
messages there for others to pick up later, allowing for asynchronous communication.
3. Sockets: The primary method for communication over a network. They act as endpoints
for sending or receiving data between processes on the same or different computers.
4. Signals: Simple notifications sent by the OS or a process to another process to alert it of
an event (e.g., "stop" or "error").
5. Semaphores & Mutexes: Variables used to control access to shared resources, ensuring
only one process uses a critical section at a time.
Computation Speedup: A large task can be split into smaller sub-tasks running in parallel,
which then communicate to combine results.
Modularity: Complex systems (like a web browser) are broken into separate processes to
improve reliability; if one tab crashes, the whole browser doesn't have to.
Convenience: Allows users to perform multiple tasks at once, like listening to music
while editing a document.
Critical Section
A critical section is a part of a program where shared resources (like memory, files, or variables)
are accessed by multiple processes or threads. To avoid problems such as race conditions and
data inconsistency, only one process/thread should execute the critical section at a time using
synchronization techniques. This ensures that operations on shared resources are performed
safely and predictably.
2. Critical Section: The actual code where shared resources are accessed or modified.
3. Exit Section: The process releases the lock or semaphore, allowing other processes to enter
the critical section.
4. Remainder Section: The rest of the program that does not involve shared resource access.
Critical Section Problem
Shared Resources and Race Conditions
A race condition occurs when two or more processes attempt to update shared data at
the same time, leading to unexpected results. Example: Two bank transactions
modifying the same account balance simultaneously without synchronization may lead
to incorrect final balance.
do{
flag=1;
while(flag); // (entry section)
// critical section
if (!flag)
// remainder section
} while(true);
Requirements of a Solution
A good critical section solution must ensure:
2. Progress
If no process is in the critical section, and some processes want to enter, the choice of
who enters next should not be postponed indefinitely.
Ensures that the system continues to make progress rather than getting stuck.
3. Bounded Waiting
There must be a limit on how long a process waits before it gets a chance to enter the
critical section.
Prevents starvation, where one process is repeatedly bypassed while others get to
execute.
Example Use Case: Older operating systems or embedded systems where simplicity and
reliability outweigh responsiveness.
acquireLock();
Process Critical Section
releaseLock();
A thread must acquire a lock prior to executing a critical section. The lock can be acquired by
only one thread. There are various ways to implement locks in the above pseudo-code.
Issue if not handled: Two simultaneous withdrawals could result in an incorrect final
balance due to race conditions.
Issue if not handled: Two users may be shown the same available seat and both may
book it, leading to overbooking.
Issue if not handled: Print jobs may get mixed up or skipped if multiple users send jobs
simultaneously.
File Editing in Shared Documents (e.g., Google Docs, MS Word with shared access)
Critical Section: Saving or writing to the shared document.
Issue if not handled: Simultaneous edits could lead to conflicting versions or data loss.
A race condition occurs when two or more processes or threads access and modify the
same data at the same time, and the final result depends on the order in which they run.
Without proper coordination, this can lead to incorrect or unpredictable results. For
example, if two people update the same bank account simultaneously without checking
each other’s changes, the final balance may be wrong.
Key Concepts
Race Condition
A race condition occurs when two or more processes or threads access and modify the
same data at the same time, and the final result depends on the order in which they
run. Without proper coordination, this can lead to incorrect or unpredictable results.
For example, if two people update the same bank account simultaneously without
checking each other’s changes, the final balance may be wrong.
Key Concepts
Shared Resource: A variable, file, memory location, or device accessed by multiple
processes.
Explanation:
P1 reads balance = 100 and prepares to add 10.
Before P1 updates the balance with the new value (110), it is interrupted by the process
P2.
P2, unaware of P1’s action (of adding 10), reads the balance as 100 (incorrect) and
prepares to subtract 10.
After subtracting, P2 updates the balance to 90 and then P1 resumes and writes the
balance as 110 which is incorrect now.
In many cases, the final balance may incorrectly be 110 or 90, instead of the expected
100. This is a classic race condition.
Unpredictable Behavior: The output may vary every time the program runs.
System Crashes: Critical system data may get corrupted, leading to failures.
Prevention Techniques
1. Mutex (Mutual Exclusion): Ensure only one process can enter the critical section at a
time.
6. Proper Scheduling: Ensure the scheduler does not preempt critical section execution.
Mutual Exclusion
During concurrent execution of processes, processes need to enter the critical section (or
the section of the program shared across processes) at times for execution. It might
happen that because of the execution of multiple processes at once, the values stored in
the critical section become inconsistent. In other words, the values depend on the
sequence of execution of instructions - also known as a race condition. The primary task
of process synchronization is to get rid of race conditions while executing the critical
section .
What is Mutual Exclusion?
Mutual Exclusion is a property of process synchronization that states that "no two
processes can exist in the critical section at any given point of time". The term was first
coined by Dijkstra. Any process synchronization technique being used must satisfy the
property of mutual exclusion, without which it would not be possible to get rid of a race
condition.
The need for mutual exclusion comes with concurrency. There are several kinds of
concurrent execution:
Interrupt handlers
Examples of such resources include files, I/O devices such as printers, and shared data
structures.
It is not advisable to make assumptions about the relative speeds of the unstable
processes.
For access to the critical section, a process that is outside of it must not obstruct another
process.
Its critical section must be accessible by multiple processes in a finite amount of time;
multiple processes should never be kept waiting in an infinite loop.
Software Method: Leave the responsibility to the processes themselves. These methods
are usually highly error-prone and carry high overheads.
Hardware Method: Special-purpose machine instructions are used for accessing shared
resources. This method is faster but cannot provide a complete solution. Hardware
solutions cannot give guarantee the absence of deadlock and starvation.
A process remains inside its critical section for a bounded time only.
A process cannot prevent any other process from entering into a critical section.
A process must not be indefinitely postponed from entering its critical section.
To put it simply, whenever node "i" wants to be removed, node "with - 1"'s subsequent
reference is changed to point to node "ith + 1" at that time. Two distinct nodes can be
removed by two threads at the same time when a shared linked list is being used by
many threads. This occurs when the first thread modifies node "ith - 1" next reference,
pointing towards the node "ith + 1," and the second thread modifies node "ith" next
reference, pointing towards the node "ith + 2." Although both nodes have been removed,
the linked list's required state has not yet been reached because node "i + 1" still exists
in the list because node "ith - 1" next reference still points to it.
Now, this situation is called a race condition. Race conditions can be prevented by
mutual exclusion so that updates at the same time cannot happen to the very bit about
the list.
Example:
In the clothes section of a supermarket, two people are shopping for clothes.
Boy, A decides upon some clothes to buy and heads to the changing room to try them
out. Now, while boy A is inside the changing room, there is an 'occupied' sign on it -
indicating that no one else can come in. Boy B has to use the changing room too, so she
has to wait till boy A is done using the changing room.
Once boy A comes out of the changing room, the sign on it changes from 'occupied' to
'vacant' - indicating that another person can use it. Hence, boy B proceeds to use the
changing room, while the sign displays 'occupied' again.
The changing room is nothing but the critical section, boy A and boy B are two different
processes, while the sign outside the changing room indicates the process
synchronization mechanism being used.
Conclusion
In conclusion, mutual exclusion is a key concept in synchronization that ensures only one
process accesses a shared resource at a time. This prevents conflicts and data corruption,
making sure that processes run smoothly and correctly. By using mutual exclusion
mechanisms, we can create stable and reliable systems that handle multiple processes
efficiently.
Consumer is a Process that is able to consume the data/item produced by the Producer.
Both Producer and Consumer share a common memory buffer. This buffer is a space of a
certain size in the memory of the system which is used for storage. The producer
produces the data into the buffer and the consumer consumes the data from the buffer.
1. Producer Process should not produce any data when the shared buffer is full.
2. Consumer Process should not consume any data when the shared buffer is empty.
3. The access to the shared buffer should be mutually exclusive i.e at a time only one
process should be able to access the shared buffer and make changes to it.
For consistent data synchronization between Producer and Consumer, the above
problem should be resolved.
Semaphores are variables used to indicate the number of resources available in the
system at a particular time. semaphore variables are used to achieve `Process
Synchronization.
Full
The full variable is used to track the space filled in the buffer by the Producer process. It
is initialized to 0 initially as initially no space is filled by the Producer process.
Empty
The Empty variable is used to track the empty space in the buffer. The Empty variable is
initially initialized to the BUFFER-SIZE as initially, the whole buffer is empty.
Mutex
Mutex is used to achieve mutual exclusion. mutex ensures that at any particular time
only the producer or the consumer is accessing the buffer.
We will use the Signal() and wait() operation in the above-mentioned semaphores to
arrive at a solution to the Producer-Consumer problem.
Signal() - The signal function increases the semaphore value by 1. Wait() - The wait
operation decreases the semaphore value by 1.
void Producer(){
while(true){
wait(Empty);
wait(mutex);
add();
signal(mutex);
signal(Full);
wait(Empty) - Before producing items, the producer process checks for the empty space
in the buffer. If the buffer is full producer process waits for the consumer process to
consume items from the buffer. so, the producer process executes wait(Empty) before
producing any item.
wait(mutex) - Only one process can access the buffer at a time. So, once the producer
process enters into the critical section of the code it decreases the value of mutex by
executing wait(mutex) so that no other process can access the buffer at the same time.
add() - This method adds the item to the buffer produced by the Producer process. once
the Producer process reaches add function in the code, it is guaranteed that no other
process will be able to access the shared buffer concurrently which helps in data
consistency.
signal(mutex) - Now, once the Producer process added the item into the buffer it
increases the mutex value by 1 so that other processes which were in a busy-waiting
state can access the critical section.
signal(Full) - when the producer process adds an item into the buffer spaces is filled by
one item so it increases the Full semaphore so that it indicates the filled spaces in the
buffer correctly.
void Consumer() {
while(true){
wait(Full);
wait(mutex);
consume();
signal(mutex);
signal(Empty);
wait(Full) - Before the consumer process starts consuming any item from the buffer it
checks if the buffer is empty or has some item in it. So, the consumer process creates
one more empty space in the buffer and this is indicated by the full variable. The value
of the full variable decreases by one when the wait(Full) is executed. If the Full variable
is already zero i.e the buffer is empty then the consumer process cannot consume any
item from the buffer and it goes in the busy-waiting state.
wait(mutex) - It does the same as explained in the producer process. It decreases the
mutex by 1 and restricts another process to enter the critical section until the consumer
process increases the value of mutex by 1.
consume() - This function consumes an item from the buffer. when code reaches the
consuming () function it will not allow any other process to access the critical section
which maintains the data consistency.
signal(mutex) - After consuming the item it increases the mutex value by 1 so that other
processes which are in a busy-waiting state can access the critical section now.
signal(Empty) - when a consumer process consumes an item it increases the value of the
Empty variable indicating that the empty space in the buffer is increased by 1.
wait(mutex);
wait(mutex) decreases the value of mutex by 1. so, suppose a process P1 tries to enter
the critical section when mutex value is 1. P1 executes wait(mutex) and decreases the
value of mutex. Now, the value of mutex becomes 0 when P1 enters the critical section
of the code.
Now, suppose Process P2 tries to enter the critical section then it will again try to
decrease the value of mutex. But the mutex value is already 0. So, wait(mutex) will not
execute, and P2 will now keep waiting for P1 to come out of the critical section.
signal(mutex)
signal(mutex) increases the value of mutex by [Link] value again becomes 1. Now, the
process P2 which was in a busy-waiting state will be able to enter the critical section by
executing wait(mutex).
In the above section in both the Producer process code and consumer process code, we
have the wait and signal operation on mutex which helps in mutual exclusion and solves
the problem of the Producer consumer process.
Conclusion
Producer Process produces data item and consumer process consumes data item.
Both producer and consumer processes share a common memory buffer.
Not more than one process should access the buffer at a time i.e mutual exclusion
should be there.
Full semaphore checks for the number of filled space in the buffer by the producer
process
Empty semaphore checks for the number of empty spaces in the buffer.
Semaphores
A semaphore is a synchronization tool used in operating systems to manage access to
shared resources in a multi-process or multi-threaded environment. It is an integer
variable that controls process execution using atomic operations like wait() and signal().
Semaphores help prevent race conditions and ensure proper coordination between
processes.
1. wait(S)
Wait Operation
If the value is less than 0, the process waits until the resource is available.
2. signal(S)
Signal Operation
State 3: If P2 now wants to enter, it cannot proceed since S = 0. It must wait until S > 0.
State 4: When P1 finishes, it performs signal(S), making S = 1. Now P2 can enter its
critical section and again sets S = 0.
This mechanism guarantees mutual exclusion, ensuring that only one process can access
the shared resource at a time, see the image below for reference:
Features of Semaphores
Mutual Exclusion: Semaphore ensures that only one process accesses a shared resource
at a time.
Reader-Writer Problem: Allows multiple readers but restricts the writers until no reader
is present.
Types of Semaphores
Semaphores are mainly of two Types:
1. Counting Semaphore
A counting semaphore can have values ranging from 0 to any positive integer. It is used
when multiple instances of a resource are available and need to be managed.
2. Binary Semaphore
A binary semaphore has only two possible values: 0 and 1. It is mainly used for mutual
exclusion, ensuring that only one process enters the critical section at a time.
Value is either 0 or 1.
Limitations of Semaphores
Priority Inversion: A low-priority process holding a semaphore can block a high-priority
one.
Deadlock: Processes may wait on each other’s semaphores in a cycle, causing indefinite
blocking.
Complex to Manage: The OS must carefully track wait and signal calls; misuse can cause
errors.
Busy Waiting: In basic implementations, processes may keep checking the semaphore
value, wasting CPU time.
Event Counters
In operating systems, event counters are synchronization primitives used to manage the
sequencing and timing of processes. Think of them as a secure, "read-only" style of
counter that allows processes to wait for a specific state or event to occur without the
complexities (and potential pitfalls) of traditional locks.
They were famously introduced as a way to handle synchronization without the need
for mutual exclusion (where only one person can touch the variable at a time).
by 1 (
Await(E, v): This is the "wait" command. The calling process is suspended until the value
of
is greater than or equal to
Key Characteristics
Feature Description
Monotonicity The value only ever goes up. It never decreases or resets during a
cycle.
Simplicity They focus on "when" something happens rather than "who" gets
to do it.
3. The "Lost Wake-up" Problem: Event counters inherently solve the lost wake-up problem.
Since the counter always increases, a process checking Await(E, 5) will proceed even if
the Advance happened while the process was busy doing something else.
Producer Logic:
4. Advance(In).
Consumer Logic:
1. Await(In, sequence_number).
3. Advance(Out).
Monitors
Monitors are a high-level synchronization mechanism that simplify process and thread
synchronization. They are built on top of locks and are mostly used in multithreading
systems like Java.
Unlike semaphores, where the programmer must explicitly call wait() and signal(),
monitors combine shared data and the operations on that data inside a single structure,
making synchronization safer and easier to manage.
Key Points:
A monitor is similar to a class/module that groups shared variables and the functions
that operate on them.
Only one thread can execute inside a monitor at a time, ensuring automatic mutual
exclusion.
In Java, monitor-like behavior is achieved using the synchronized keyword ensures that
only one thread can execute inside the monitor at a time.
A monitor encapsulates both shared data (critical resource) and the operations that
access or modify it.
Structure of Monitor
wait(): temporarily releases the monitor lock and puts the thread to sleep until it is
signaled.
Message Passing
So message passing means how a message can be sent from one end to the other end.
Either it may be a client-server model or it may be from one node to another node. The
formal model for distributed message passing has two timing models one is synchronous
and the other is asynchronous.
2. The pattern of the connection provided by the channel is described by some topology
systems.
4. So by the definition of distributed systems, we know that they are geographically set of
computers. So it is not possible for one computer to directly connect with some other
node.
6. The sender decides what data has to be sent over the network. An example is, making a
phone call.
7. The data is only fully communicated after the destination worker decides to receive the
data. Example when another person receives your call and starts to reply to you.
8. There is no time barrier. It is in the hand of a receiver after how many rings he receives
your call. He can make you wait forever by not picking up the call.
9. For successful network communication, it needs active participation from both sides.
2. Each node might not know who is at another end. So in this way, the topology would be
arranged.
Additional Information:
1. Security Measures: It is important to add security measures in message passing as the
data being sent is vulnerable to attacks. Hence, techniques such as encryption and
authentication should be used to ensure secure communication.
2. Types of Messages: The article could mention the types of messages that can be sent in
the message passing model, such as request messages, reply messages, broadcast
messages, and multicast messages.
4. Error Handling: The article can mention the error handling mechanism used in message
passing, as errors can occur during communication. Error handling ensures that the
system can continue its operation even in the presence of errors.
5. Communication Protocols: The article can also mention the various communication
protocols used in message passing, such as TCP/IP, UDP, and RDP.
6. Comparison with other models: It would be interesting to compare the message passing
model with other models, such as shared memory and Remote Procedure Call (RPC)
models. This will help readers understand the advantages and disadvantages of message
passing over other models.
3. Data transfer usually requires cooperative operations which can be difficult to achieve.
4. It is difficult for programmers to develop portable applications using this model because
message-passing implementations commonly comprise a library of subroutines that are
embedded in source code. Here again, the programmer has to do everything on his own.
These problems highlight the need for proper synchronization techniques like
semaphores, mutexes, and monitors.
1. Producer-Consumer Problem
This problem involves two processes:
Challenges:
Buffer Overflow – producer tries to add when the buffer is full.
2. Reader-Writer Problem
Here, multiple processes read and write to a shared resource.
Challenges:
Solutions:
Challenges:
Deadlock – if all philosophers pick up one chopstick and wait for the other.
Solution: Use semaphores or monitors to coordinate chopstick use and avoid deadlock.
Challenges:
Solution: Semaphores can manage customer queues, chair availability, and barber
activity.
Deadlocks
A deadlock is a specific situation in computing and multitasking where a group of
processes is permanently blocked because each process is holding a resource and
waiting for another resource held by another process in the group.
Think of it like a four-way traffic jam where every car is waiting for the one in front of it
to move, but nobody can move because the intersection is blocked.
Mutual Exclusion: At least one resource must be held in a non-shareable mode (only
one process can use it at a time).
Hold and Wait: A process must be holding at least one resource and waiting to acquire
additional resources currently held by other processes.
Circular Wait: A closed chain of processes exists such that each process holds at least
one resource needed by the next process in the chain.
2. Deadlock Prevention
Prevention works by ensuring that at least one of the Coffman conditions never happens.
It is a proactive, "strict" approach.
Eliminate Mutual Exclusion: Difficult for hardware (like printers), but easier for read-
only files.
Eliminate Hold and Wait: Require processes to request all required resources at the
start. This leads to low resource utilization.
Allow Preemption: If a process is denied a new request, it must release all its current
resources so others can use them.
Prevent Circular Wait: Impose a strict ordering of resource types (e.g., Resource A must
always be requested before Resource B).
The Banker's Algorithm is the most famous method for doing this.
Safe State: There exists at least one sequence (a "Safe Sequence") where every process
can get its maximum resources, finish its job, and return the resources to the pool.
Unsafe State: There is no guaranteed sequence to finish all processes. This does
not mean a deadlock has occurred yet, but the system can no longer guarantee one
won't happen.
Structure Description
Available A vector representing how many units of each resource type are
currently free.
Max A matrix showing the maximum demand of each process for each
resource.
Need A matrix (
1. Check Request: If the request is greater than the process's remaining Need or the
system's Available resources, the request is denied or delayed.
2. Pretend to Allocate: The Banker "pretends" to grant the resources by updating the state:
1.
2.
3.
3. Safety Check: The Banker runs a Safety Algorithm on this "pretend" state.
4. Decision:
2. If the state is Unsafe, the process is made to wait, and the "pretend" allocation is rolled
back.
4. Example: A "Safe" vs. "Unsafe" Decision
Imagine a system with 10 Total Units of a resource (e.g., RAM blocks).
Current Snapshot:
9 2 7
4 2 2
7 3 4
Total Allocated:
Available:
Scenario:
1. Pretend: Give
2. Safety Test:
1. Can we finish
). New Available =
3. Can we finish
4. When
5. Can we finish
5. Limitations
While theoretically sound, the Banker's Algorithm is rarely used in general-purpose OSs
(like Windows or Linux) because:
It requires processes to know their Maximum Need in advance, which is almost
impossible for modern software.
Detection
The system maintains a Resource Allocation Graph (RAG).
In systems with a single instance of each resource type, a cycle in the graph indicates a
deadlock.
Recovery
Once a deadlock is detected, the system must break it using one of two methods:
1. Process Termination:
2. Resource Preemption:
Selecting a victim: Decide which process to take resources from based on cost.
Rollback: Return the process to a previous "safe state" and restart it from there.
Starvation: Ensure the same process isn't always picked as a victim.