Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
▶
Steps
setup
Initialize variables and calculate Need matrix
Calculate the Need matrix by subtracting Allocation from Max Demand for each process and resource type.
💡 The Need matrix shows how many more resources each process may request, which is essential for safety checks.
Line:need = [[max_demand[i][j] - allocation[i][j] for j in range(m)] for i in range(n)]
💡 Understanding the Need matrix is key to knowing what resources processes still require.
compare
Check if request exceeds process's maximum need
Verify if the requested resources exceed the process's remaining Need. If yes, deny immediately.
💡 Requests beyond maximum declared need are invalid and unsafe.
Line:if any(request[j] > need[process_id][j] for j in range(m)):
return False
💡 This step prevents processes from requesting more than their declared maximum, ensuring system integrity.
compare
Check if requested resources are available
Verify if the system has enough available resources to satisfy the request immediately.
💡 Resources must be available to grant the request; otherwise, the process must wait.
Line:if any(request[j] > available[j] for j in range(m)):
return False
💡 This step ensures the system does not allocate more resources than it currently has.
insert
Tentatively allocate requested resources
Subtract requested resources from available and add them to the process's allocation temporarily.
💡 Tentative allocation simulates granting the request to check if the system remains safe.
Line:available_temp = [available[j] - request[j] for j in range(m)]
allocation_temp = [row[:] for row in allocation]
for j in range(m):
allocation_temp[process_id][j] += request[j]
💡 This step prepares a hypothetical state to test safety without committing changes.
compare
Check if tentative state is safe
Call is_safe_state to verify if the system can allocate resources without deadlock.
💡 Safety check ensures that all processes can finish eventually with the tentative allocation.
💡 This step is the core of Banker's Algorithm, preventing unsafe allocations.
insert
Commit resource allocation
Update the actual available and allocation arrays to reflect the granted request.
💡 Committing the allocation means the system officially grants the resources to the process.
Line:for j in range(m):
available[j] -= request[j]
allocation[process_id][j] += request[j]
💡 This step finalizes the resource allocation after confirming safety.
reconstruct
Return True indicating request granted safely
The algorithm returns True, confirming the request can be safely granted.
💡 Returning True signals the system and process that the resource request succeeded safely.
Line:return True
💡 The algorithm's final decision is based on the safety check and committed allocation.
reconstruct
Summary of final resource state
Show the updated Available and Allocation matrices after granting the request.
💡 This final state confirms the system's resource distribution after the request.
💡 You see how the system's resources have changed due to the request.
reconstruct
Visualize updated Available resources
Available resources after allocation: [2, 3, 0].
💡 Available resources decrease by the request amount, reflecting the committed allocation.
💡 You see the system's current free resources after granting the request.
reconstruct
Visualize updated Allocation for process P1
Allocation for P1 after request: [3, 0, 2].
💡 The process's allocation increases by the requested resources, reflecting the granted request.
💡 You see how the process's resource holdings have changed.
def bankers_algorithm(available, max_demand, allocation, request, process_id):
n = len(max_demand) # Number of processes # STEP 1
m = len(available) # Number of resource types # STEP 1
need = [[max_demand[i][j] - allocation[i][j] for j in range(m)] for i in range(n)] # STEP 1
# Check if request is valid # STEP 2
if any(request[j] > need[process_id][j] for j in range(m)):
return False # Request exceeds maximum demand
if any(request[j] > available[j] for j in range(m)): # STEP 3
return False # Resources not available
# Tentatively allocate resources # STEP 4
available_temp = [available[j] - request[j] for j in range(m)]
allocation_temp = [row[:] for row in allocation]
for j in range(m):
allocation_temp[process_id][j] += request[j]
# Check if new state is safe # STEP 5
if is_safe_state(available_temp, max_demand, allocation_temp):
# Commit allocation # STEP 6
for j in range(m):
available[j] -= request[j]
allocation[process_id][j] += request[j]
return True # STEP 7
else:
return False
📊
Banker's Algorithm - Safe State & Resource Allocation - Watch the Algorithm Execute, Step by Step
Watching each step of the algorithm helps you understand how resource needs, availability, and safety checks interact to prevent deadlocks.
Transitionterminated → terminated pid:1 - final state
0
ready
1
terminated
2
ready
3
ready
4
ready
Ready Queue
0234
Waiting Queue
empty
🖥CPUidlet=7
idle
1
Transitionterminated → terminated pid:1 - show available resources
0
ready
1
terminated
2
ready
3
ready
4
ready
Ready Queue
0234
Waiting Queue
empty
🖥CPUidlet=8
idle
1
Transitionterminated → terminated pid:1 - show allocation update
0
ready
1
terminated
2
ready
3
ready
4
ready
Ready Queue
0234
Waiting Queue
empty
🖥CPUidlet=9
idle
1
Key Takeaways
✓ The Need matrix is fundamental to understanding what resources each process can still request.
Without visualizing Need, it's hard to grasp why some requests are invalid or unsafe.
✓ Tentative allocation allows the algorithm to simulate granting a request before committing, preventing unsafe states.
Seeing this step clarifies how Banker's Algorithm avoids deadlocks by cautious resource granting.
✓ The safety check is the core decision point that determines if the system remains deadlock-free after allocation.
Watching the safety check outcome helps understand why some requests are denied even if resources appear available.
Practice
(1/5)
1. In a system where multiple producers and consumers share a fixed-size buffer, which synchronization mechanism best ensures that producers wait when the buffer is full and consumers wait when the buffer is empty?
easy
A. Mutex lock only to protect buffer access
B. Spinlocks to busy-wait until buffer space is available
C. Counting semaphores to track empty and full slots along with a mutex for mutual exclusion
D. Condition variables without any semaphore or mutex
Solution
Step 1: Identify synchronization needs
Producers must wait if the buffer is full; consumers must wait if empty. This requires tracking counts of empty and full slots.
Step 2: Role of counting semaphores
Counting semaphores elegantly track available slots (empty) and filled slots (full), enabling blocking waits without busy-waiting.
Step 3: Need for mutual exclusion
A mutex is required to protect concurrent access to the buffer itself to avoid race conditions.
Step 4: Why other options fail
Mutex alone (A) cannot block producers/consumers based on buffer state; spinlocks (C) waste CPU cycles; condition variables (D) require mutexes and signaling to work correctly in this scenario.
Final Answer:
Option C -> Option C
Quick Check:
Counting semaphores + mutex is the classic producer-consumer solution [OK]
2. Which of the following statements best explains why SSTF can lead to starvation, and why SCAN or C-SCAN are preferred in heavy-load scenarios?
medium
A. SSTF picks the closest request, potentially ignoring far requests indefinitely; SCAN and C-SCAN guarantee servicing all requests by sweeping the disk head across all tracks.
B. SSTF always services requests in arrival order, causing long waits for distant requests; SCAN and C-SCAN service requests in a fixed direction to prevent this.
C. SSTF has higher overhead due to sorting requests; SCAN and C-SCAN have lower overhead and thus better performance under load.
D. SSTF requires knowledge of all requests upfront; SCAN and C-SCAN can operate with partial knowledge, making them more scalable.
Solution
Step 1: Understand SSTF starvation
SSTF always picks the nearest request, so requests far from the current head position may wait indefinitely.
Step 2: SCAN and C-SCAN fairness
They move the head in a fixed direction, servicing all requests in order, ensuring no starvation.
Step 3: Evaluate other options
Options B, C, and D contain incorrect claims: SSTF does not service requests in arrival order; it does not have higher overhead due to sorting; and it requires knowledge of all requests upfront similarly to SCAN and C-SCAN.
Final Answer:
Option A -> Option A
Quick Check:
SSTF picks the closest request, potentially ignoring far requests indefinitely; SCAN and C-SCAN guarantee servicing all requests by sweeping the disk head across all tracks. correctly explains starvation and fairness trade-offs.
Hint: SSTF starves distant requests; SCAN/C-SCAN sweep all tracks fairly
Common Mistakes:
Confusing SSTF with FCFS regarding request order
Assuming SSTF has higher computational overhead
Believing SCAN/C-SCAN require less request knowledge
3. In a system with multiple CPUs (multi-core), how does context switching overhead differ compared to a single-core system, and what additional factors must be considered?
hard
A. Context switching overhead per CPU remains the same, but inter-CPU cache coherence and synchronization add extra overhead
B. Context switching overhead is eliminated because each CPU runs its own process independently
C. Context switching overhead doubles because each CPU must save and restore registers twice
D. Context switching overhead is reduced because the scheduler can switch processes faster across CPUs
Solution
Step 1: Per-CPU context switch cost
Each CPU still saves/restores registers during context switches, so overhead per CPU is similar to single-core.
Step 2: Additional multi-core factors
Multi-core systems require cache coherence protocols and synchronization mechanisms, adding overhead beyond single-core context switching.
Step 3: Misconceptions
Context switching is not eliminated; CPUs do not save/restore registers twice per switch; scheduler speedup is not guaranteed.
Believing scheduler is always faster on multi-core
4. If Peterson's algorithm is used on a modern multi-core processor with weak memory ordering (e.g., relaxed memory consistency), what is a likely issue that can arise, and how can it be addressed?
hard
A. The algorithm will cause starvation because weak memory ordering breaks bounded waiting
B. The algorithm will cause deadlock because weak memory ordering delays flag updates
C. The algorithm will work correctly without modification because it uses only atomic variables
D. The algorithm may fail mutual exclusion due to instruction reordering; inserting memory barriers can fix this
Solution
Step 1: Understand weak memory ordering impact
Modern processors may reorder instructions, causing visibility issues for shared variables.
Step 2: Effect on Peterson's algorithm
Without memory barriers, the flags and turn variables may be seen out of order, breaking mutual exclusion.
Step 3: Solution
Inserting memory fences/barriers enforces ordering and visibility, preserving correctness.
Final Answer:
Option D -> Option D
Quick Check:
Memory barriers are essential on weakly ordered architectures for software synchronization.
Hint: Weak memory ordering -> need memory barriers for Peterson's correctness
Common Mistakes:
Assuming Peterson's works without modification on all hardware
5. If a system uses LRU page replacement but the reference string exhibits a cyclic pattern larger than the number of frames, what is the expected impact on page faults compared to FIFO?
hard
A. LRU will have fewer page faults because it always evicts the least recently used page
B. LRU will perform optimally and minimize page faults in cyclic patterns
C. LRU will have more page faults than FIFO because it keeps evicting pages that will be needed soon
D. LRU and FIFO will have similar page fault rates due to cyclic references exceeding frame count
Solution
Step 1: Understand cyclic reference pattern larger than frames
Pages are referenced in a cycle longer than available frames, causing repeated evictions.
Step 2: Analyze LRU vs FIFO behavior
Both algorithms will evict pages that will be needed soon because the working set exceeds frame count. LRU's advantage diminishes as no page stays long enough to be reused before eviction.
Step 3: Evaluate options
LRU will have fewer page faults because it always evicts the least recently used page is false; LRU advantage is lost in cyclic patterns larger than frames. LRU will perform optimally and minimize page faults in cyclic patterns is false; both have similar fault rates in this scenario. LRU will have more page faults than FIFO because it keeps evicting pages that will be needed soon is false; LRU does not necessarily have more faults than FIFO here. LRU and FIFO will have similar page fault rates due to cyclic references exceeding frame count is true; LRU and FIFO have similar fault rates due to cyclic references exceeding frame count.
Final Answer:
Option D -> Option D
Quick Check:
When working set > frames, LRU and FIFO fault rates converge.
Hint: LRU loses advantage when working set exceeds frames [OK]