💡 Indirect blocks store pointers to data blocks, extending the inode's reach.
fill_cells
Assign data blocks via single indirect pointer
Data blocks 6 through 14 are assigned through the single indirect block, filling its pointers to these blocks.
💡 Single indirect blocks hold pointers to data blocks beyond direct pointers' capacity.
Line:for i in range(NUM_DIRECT_POINTERS, blocks_needed):
inode.single_indirect.pointers[i - NUM_DIRECT_POINTERS] = allocate_data_block()
💡 Indirect blocks allow the inode to reference many more data blocks efficiently.
fill_cells
Finalize inode metadata with total blocks
The inode's file size metadata is updated to reflect the total number of blocks assigned (15 blocks).
💡 Updating metadata ensures the file system knows the file's actual size.
Line:inode.file_size = blocks_needed * BLOCK_SIZE
💡 Metadata must be consistent with the block pointers assigned.
setup
Complete inode initialization
The inode initialization process completes, and the inode is ready with metadata and block pointers assigned.
💡 Completing initialization means the inode can now be used by the file system to access file data.
Line:return inode
💡 The inode is now fully prepared to represent the file on disk.
class Inode:
def __init__(self):
self.file_size = 0 # STEP 2
self.owner_uid = None # STEP 2
self.direct_blocks = [None] * 6 # STEP 3
self.single_indirect = None # STEP 7
self.double_indirect = None
self.triple_indirect = None
def initialize_metadata(self):
self.file_size = 0 # STEP 2
self.owner_uid = get_current_user_id() # STEP 2
def allocate_data_block():
# Returns a new data block number
pass
def allocate_indirect_block():
# Returns a new indirect block structure
return IndirectBlock()
class IndirectBlock:
def __init__(self):
self.pointers = [None] * 9 # example size
def create_inode_with_blocks(blocks_needed):
inode = Inode() # STEP 1
inode.initialize_metadata() # STEP 2
# Assign direct blocks
for i in range(6): # STEP 5
inode.direct_blocks[i] = allocate_data_block() # STEP 4 and 5
if blocks_needed > 6: # STEP 6
inode.single_indirect = allocate_indirect_block() # STEP 7
for i in range(6, blocks_needed): # STEP 8
inode.single_indirect.pointers[i - 6] = allocate_data_block()
inode.file_size = blocks_needed * 4096 # STEP 9
return inode # STEP 10
📊
Inode Structure - File Metadata & Block Pointers - Watch the Algorithm Execute, Step by Step
Watching this step-by-step helps you understand how file metadata and block pointers are organized and linked in an inode, which is fundamental for file system operations.
✓ Inodes store both file metadata and pointers to data blocks, enabling efficient file system access.
This insight is hard to see from code alone because the linkage between metadata and pointers is conceptual and structural.
✓ Direct pointers are used first for fast access, and indirect pointers extend the file size capacity by adding levels of indirection.
Visualizing pointer assignment order clarifies why indirect pointers exist and how they expand addressing.
✓ The decision to use indirect pointers depends on the file size exceeding direct pointer capacity, illustrating a key branching in inode design.
Seeing this decision step helps concretely understand when and why the inode structure changes.
Practice
(1/5)
1. Why is it generally inefficient to keep a process in the Ready state for a long time without scheduling it to Running, especially in a multi-core system?
medium
A. Because the process wastes CPU cache locality and increases context switch overhead when scheduled later
B. Because the process holds resources like memory and I/O devices exclusively while Ready
C. Because the process consumes CPU cycles even in Ready state, reducing overall throughput
D. Because the process cannot perform I/O operations while in Ready state, causing system deadlocks
Solution
Step 1: Understand Ready state resource usage
Processes in Ready state do not consume CPU cycles but occupy scheduling queues.
Step 2: Analyze each option
A: Correct. Long Ready times cause loss of CPU cache locality and increase context switch overhead when finally scheduled. B: Incorrect. Processes do not hold exclusive I/O or memory resources just by being Ready. C: Incorrect. Ready processes do not consume CPU cycles. D: Incorrect. Ready processes do not cause deadlocks by not performing I/O.
Final Answer:
Option A -> Option A
Quick Check:
Ready state delays hurt cache locality and increase context switch costs [OK]
Hint: Ready state processes wait without CPU but lose cache locality and increase context switch overhead [OK]
Common Mistakes:
Believing Ready processes consume CPU cycles
Assuming Ready processes hold exclusive resources
Confusing Ready state with Waiting state regarding I/O
2. Which of the following statements about turnaround time in Round Robin scheduling is INCORRECT?
medium
A. Turnaround time is the total time from process submission to completion
B. Turnaround time includes both waiting time and execution time of a process
C. Turnaround time can be less than the CPU burst time if the quantum is large
D. Turnaround time depends on the time quantum size and the number of processes in the ready queue
Solution
Step 1: Define turnaround time
Turnaround time = completion time - arrival time, always ≥ CPU burst time.
Step 2: Analyze each option
Turnaround time includes both waiting time and execution time of a process is correct; turnaround time includes waiting and execution. Turnaround time is the total time from process submission to completion is correct by definition. Turnaround time depends on the time quantum size and the number of processes in the ready queue is correct; quantum size and queue length affect waiting and thus turnaround time. Turnaround time can be less than the CPU burst time if the quantum is large is incorrect because turnaround time cannot be less than CPU burst time regardless of quantum size.
Final Answer:
Option C -> Option C
Quick Check:
Turnaround time ≥ CPU burst time always.
Hint: Turnaround time ≥ CPU burst time
Common Mistakes:
Confusing turnaround time with waiting time
Thinking large quantum can reduce turnaround below burst time
Ignoring impact of queue length on turnaround
3. Which of the following statements about Effective Access Time (EAT) in systems using TLB is INCORRECT?
medium
A. A TLB miss always causes a page fault, increasing EAT drastically
B. EAT depends on both TLB hit ratio and memory access time
C. EAT can be calculated as (TLB hit ratio x TLB access time) + (TLB miss ratio x page table access time)
D. Improving TLB hit ratio reduces the average memory access time
Solution
Step 1: Recall EAT formula
EAT = (hit ratio x access time on hit) + (miss ratio x access time on miss)
Step 2: Analyze each statement
A: Correct, EAT depends on hit ratio and memory times. B: Incorrect, TLB miss does not always cause page fault; it triggers page table lookup. C: Correct, formula reflects hit and miss costs. D: Correct, higher hit ratio lowers average access time.
Final Answer:
Option A -> Option A
Quick Check:
TLB miss ≠ page fault; page fault only if page not in memory.
Hint: TLB miss ≠ page fault; page fault only if page absent [OK]
Common Mistakes:
Confusing TLB miss with page fault
Misapplying EAT formula
4. Suppose a multithreaded application crashes due to a segmentation fault. Which of the following scenarios best explains why debugging is more challenging compared to a multi-process application?
hard
A. Because threads share the same memory, a fault in one thread can corrupt shared data, making root cause analysis harder
B. Because each thread has its own memory space, faults are isolated and easier to debug
C. Because thread context switches are slower, the fault is harder to reproduce
D. Because processes share memory, faults propagate easily between them
Solution
Step 1: Understand memory sharing in threads
Threads share the same address space, so a fault in one thread can corrupt shared data affecting others.
Step 2: Contrast with processes
Processes have isolated memory, so faults are contained, making debugging easier.
Step 3: Evaluate options
Because each thread has its own memory space, faults are isolated and easier to debug is false because threads share memory. Because thread context switches are slower, the fault is harder to reproduce is false; thread context switch speed does not affect fault reproducibility. Because processes share memory, faults propagate easily between them is false; processes do not share memory by default.
Final Answer:
Option A -> Option A
Quick Check:
Shared memory in threads complicates debugging due to data corruption [OK]
Hint: Shared memory means shared bugs [OK]
Common Mistakes:
Assuming threads have isolated memory like processes
Confusing context switch speed with debugging difficulty
Believing processes share memory by default
5. If a system uses preemptive SJF scheduling but the burst times of processes are not known in advance and must be estimated, which challenge is most likely to affect scheduling accuracy?
hard
A. Incorrect burst time estimates can cause frequent unnecessary preemptions
B. The scheduler will always pick the wrong process due to estimation errors
C. Non-preemptive scheduling must be used instead to avoid errors
D. Starvation is eliminated because estimates prevent preemption
Solution
Step 1: Understand burst time estimation impact
Estimations can be inaccurate, causing the scheduler to preempt based on wrong assumptions.
Step 2: Analyze consequences
Frequent unnecessary preemptions increase overhead and reduce efficiency.
Step 3: Why other options are incorrect
B: Scheduler won't always pick wrong process; estimates can be close. C: Non-preemptive scheduling is a design choice, not forced by estimation. D: Starvation is not eliminated by estimates; it depends on scheduling policy.
Final Answer:
Option A -> Option A
Quick Check:
Estimation errors cause scheduling inefficiency via unnecessary preemptions.
Hint: Bad burst time estimates cause too many preemptions in preemptive SJF [OK]