← Research

Systems · C++

When does a disk turn a file away while it still has room?

Virtual File System

On a 1,024-block disk held at 80% for 3,000 cycles, First-Fit refuses 6.7% of writes while total free space stays near 198 blocks.

Role
Personal project — systems programming
Study
Deterministic Block Allocators for Resource-Constrained File Systems
Built with
C++ · Systems Programming · Block Allocation · Data Structures
Virtual File System, cover image

A file system implemented in C++ over a flat block device. Free space is tracked with a bitmap, files are described by inode-style metadata records, and allocation uses a First-Fit scan for contiguous runs of blocks. Building it meant confronting the trade-off that makes First-Fit interesting: it is cheap to compute and keeps small files near the start of the disk, but repeated allocate and delete cycles strand free blocks in runs too short to reuse. The simulator below is a browser reimplementation of that allocator, so the fragmentation behaviour can be watched directly.

Problem

A file system has to answer two questions quickly: which blocks are free, and where should a new file go. Answering them naively means scanning the whole disk on every write. Answering them well means picking a free-space representation and an allocation policy, then living with the failure mode each one implies.

Approach

Free space is held in a bitmap, one bit per block, so a lookup is a bit test rather than a walk over file metadata. Files carry inode-style records holding their starting block and length, which keeps contiguous files cheap to read sequentially. Allocation scans for the first run of free blocks large enough to hold the file. The cost of that choice is external fragmentation: as files are deleted, the free space that returns is scattered in runs that may each be too small for the next allocation even when the total free space is ample. The interactive simulator makes this visible — allocating and deleting repeatedly drives the fragmentation figure up while total free blocks stay unchanged. Running the same allocator against a fixed workload puts a number on it: on a 1024-block device held at 80% occupancy for 3,000 delete/allocate cycles, the longest usable run falls from 189 blocks to around 32 while total free space stays near 198, and 6.7% of writes are refused on a disk that was never actually full.

Run the allocator

Recorded benchmark · 1,024 blocks, 3,000 cycles, seeded workload · files turned awayFirst-Fit6.71%Best-Fit4.65%Worst-Fit51.61%

Launch film

Launch film · 21 seconds, with sound.

Four-tier lookup path

Four-tier lookup path
A read is four dereferences and one addition. The descriptor is only an index — every tier after it is a pointer the previous tier was holding.
Describe the diagram

How a read resolves, left to right. A file descriptor is an index into the per-process descriptor array, where entries 0 to 2 are the standard streams and entry 3 points at a file table entry. That entry holds the read and write offsets, the mode the file was opened with, a reference count, and a pointer to an inode. The inode holds the file's number, name, size, link count, type, and a pointer to its data buffer on the heap. The buffer is shown byte by byte, with the active window highlighted. Beneath, the same path is written out as four steps: index the descriptor array, check the mode and read the current offset, dereference the pointer to reach the inode, and add the offset to the buffer address to reach the target byte.