Module 4 · Unit 10 · Lesson 3 of 7
Inodes
The on-disk structure that holds a file's metadata and locates its data. On Unix-like systems, every file is identified by an inode number, and every inode records the file's attributes and the pointers to its data blocks. This lesson presents the classical inode in detail — its fields, its pointers, its lifetime, and its role in path resolution — and surveys the modern inode structures that have replaced it in high-performance file systems.
01 Introduction
Lesson 10.1 introduced the file and directory abstractions
and noted that a file's name is separate from its identity:
a directory entry maps a name to an inode number, and the
inode number identifies the file within the file system.
Lesson 10.2 examined the metadata that the inode holds —
the file's type, ownership, permissions, size, timestamps,
and the other attributes that the struct stat
interface exposes. This lesson turns to the inode itself:
the file system's own structure for holding that metadata
and for locating the file's data.
The inode (short for "index node") is the central data structure of a classical Unix file system. It is a fixed-size record that contains everything the file system knows about a file except the file's name: its type, its permission bits, its owner, its size, its timestamps, and — most importantly — the pointers to the blocks that hold the file's contents. The inode is what makes a file more than a sequence of bytes: it is the file's identity, its metadata, and its map to its data.
The inode's design is a response to a specific problem: how to represent a file whose size is not known in advance, on a storage device that allocates space in fixed-size blocks, without wasting space on files that are small and without giving up on files that are large. The classical answer is a hybrid pointer scheme: the inode holds a small number of direct pointers that point straight to the file's first data blocks, a single indirect pointer that points to a block full of pointers, a double indirect pointer that points to a block full of single-indirect blocks, and a triple indirect pointer that goes one level deeper. The scheme is efficient for small files (whose data fits in the direct pointers) and scalable for large files (whose data spills into the indirect pointers), and it has been refined but not fundamentally replaced in the decades since it was introduced.
This lesson presents the inode in detail. It defines the inode and the inode number, describes the inode table and the free-inode bitmap, and walks through the classical inode's fields and pointers. It traces the inode's lifetime from allocation to deallocation, explains the role of the link count in that lifetime, and shows how the inode is used in path resolution. It presents the VFS inode cache and the distinction between the on-disk inode and the in-memory inode. It examines the problem of inode exhaustion and the modern inode structures used in ext4, XFS, Btrfs, and ZFS. And it closes with a discussion of the trade-offs that the inode's design embodies and of the directions in which modern file systems are moving.
Prerequisites
This lesson builds directly on Lessons 10.1 and 10.2.
The file and directory abstractions, the distinction
between a file's name and its identity, and the
standard file operations are assumed from Lesson 10.1.
The metadata fields — the struct stat
structure, the type and mode bits, the timestamps, and
the permission-check algorithm — are assumed from
Lesson 10.2. The discussion of directory entries in
section 4.6 assumes the directory-as-a-file abstraction
from Lesson 10.1. No prior knowledge of inodes,
indirect block pointers, or the VFS is assumed; all
are introduced here. The discussion of free-space
management in section 4.4 assumes the fragmentation
material from Lesson 8.5, but no new concepts are
introduced.
02 Learning objectives
By the end of this lesson you will be able to
- Define an inode and its role in the Unix file system, and describe the inode table and inode number.
- Describe the classical inode layout, including the direct, single indirect, double indirect, and triple indirect block pointers.
- Compute the maximum file size supported by a given inode layout and block size.
- Explain inode allocation and deallocation, the link count, and the inode's lifetime.
- Describe the VFS inode cache and the modern inode structures used by ext4, XFS, Btrfs, and ZFS.
This lesson contributes to U10.3 and U10.4, and to course learning outcomes CLO1, CLO5, and CLO6.
03 Required reading
- Operating System Concepts — Chapter 14, Section 14.2, File-System Implementation. The standard treatment of the inode, the inode table, and the indirect block pointers.
- OSTEP — Chapter 40, File System Implementation, and Chapter 41, Locality and The Fast File System. OSTEP's treatment of the inode as the file's metadata structure and of the multi-level index is the clearest available, and its discussion of the Fast File System's refinements is the basis for the worked example in this lesson.
- Tanenbaum & Bos — Modern Operating Systems, Chapter 4, Section 4.3, File-System Implementation. The treatment of the inode, the i-node table, and the directory entry.
- Silberschatz et al. — Chapter 14, Section 14.2. Read the discussion of the inode's pointers and the maximum file size calculation.
- McKusick, M. K., Joy, W. N., Leffler, S. J., & Fabry, R. S. — A Fast File System for UNIX. ACM Transactions on Computer Systems, 1984. The paper that refined the classical inode's layout for performance, introduced cylinder groups, and established many of the design principles still in use. Read sections 2 and 3.
- Bach, M. J. — The Design of the UNIX Operating System. Prentice Hall, 1986. Chapters 4 and 5. The classical reference for the System V inode, the inode table, and the file-system operations.
- Kerrisk, M. — The Linux Programming Interface, Chapter 14, File Systems, and Chapter 15, File Attributes. The Linux view of the inode and the inode-related interfaces.
- Love, R. — Linux Kernel Development, 3rd ed., Chapter 13, The Virtual File System. The VFS inode, the inode cache, and the distinction between the on-disk inode and the in-memory inode.
-
Linux man pages:
man 2 stat,man 2 statx,man 2 link,man 2 unlink,man 2 rename,man 2 open,man 5 proc(for the/proc/<pid>/fdinterface),man 8 tune2fs(for the-loption that reports file-system statistics, including the inode count),man 8 dumpe2fs,man 1 df(for the-ioption that reports inode usage).
Reading strategy
Read the OSTEP chapter on file-system implementation first. Its presentation of the inode as a multi-level index, and its calculation of the maximum file size supported by the scheme, are the clearest available account, and they are the basis for the worked example in this lesson. Then read the Fast File System paper for the refinements that turned the classical inode into the inode we use today. The Bach book is the classical reference for the System V inode and the inode table; read it if you want the historical details. Finally, the Love chapter on the VFS is the practical view, showing how the kernel manages the in-memory inodes and how the on-disk inodes are bridged with the VFS interface. Pay particular attention to the distinction between the on-disk inode and the in-memory inode: they are related but not the same, and the difference matters for understanding the kernel's behaviour.
04 Core content
4.1 What is an inode?
An inode (index node) is a fixed-size data structure that contains all the information the file system knows about a file, except the file's name. It is the file's identity, its metadata, and — crucially — the map that locates the file's data on the storage device. Every file has exactly one inode, and every inode belongs to exactly one file.
The inode has three roles in the file system.
- Identity. The inode's number is the file's identity. The name-to-inode mapping is stored in the directory; the inode number is what the file system uses internally to refer to the file. Two names that refer to the same inode refer to the same file; two names that refer to different inodes refer to different files, even if their contents are identical.
-
Metadata. The inode holds the file's
metadata: its type, its permission bits, its owner, its
group, its size, its timestamps, and the other
attributes that the
struct statinterface exposes. This is the metadata that Lesson 10.2 examined; the inode is where it lives. - Data map. The inode holds the pointers to the file's data blocks. Without the inode, the file system would not know where on the disk the file's contents are stored. The design of the pointers — the inode layout — determines how large a file can be and how efficiently the file system can access it.
The inode is not the file's name. The name is stored in the directory, as an entry that maps the name to an inode number. The inode number is a 32-bit or 64-bit integer that identifies the inode within the file system's inode table, a contiguous array of inodes on the disk. The inode number is the index into the table: inode k is at position k in the table, so the file system can locate any inode by its number with a single computation.
The separation of the inode from the directory is the key design decision that makes the Unix file model work. It allows a file to have multiple names (hard links), because multiple directory entries can map to the same inode number. It allows a file to be renamed without changing its identity, because the rename changes the directory entry, not the inode. And it allows the file system to look up a file's metadata without reading the directory entry again, because the inode number identifies the inode directly.
4.2 The inode table and inode number
The inode table is the file system's array of inodes. It is a contiguous region of the disk whose size is determined when the file system is created: the administrator chooses how many inodes the file system will have, and the table's size is fixed for the life of the file system. Each inode is a fixed-size record — 128 bytes on ext2, 256 bytes on ext4, larger on some modern file systems — and the inode number is the index into the table.
The number of inodes is fixed at file-system creation time. This is a fundamental constraint of the classical design: a file system can hold at most as many files as it has inodes, regardless of how much free disk space it has. If the inode table is exhausted, the file system cannot create new files, even if there are gigabytes of free data blocks. This is the inode exhaustion problem, and it is a well-known operational issue on filesystems that hold many small files.
The number of inodes is chosen based on the expected workload. A file system that will hold a small number of large files (a database, a media library) needs few inodes and can use the space for data blocks instead. A file system that will hold many small files (a mail spool, a source-code repository, a web cache) needs many inodes and must reserve a proportionally larger fraction of the disk for the inode table. Modern file systems relax this constraint: ext4, for example, can allocate inodes dynamically, and ZFS and Btrfs have no inode table at all (they allocate metadata blocks on demand).
In addition to the inode table, the file system maintains an inode bitmap: a bitmap with one bit per inode, indicating whether the inode is free or allocated. The bitmap is used by the inode allocator to find a free inode when a new file is created. It is typically stored in a fixed location (or, on modern file systems, in a B-tree), and it is updated whenever an inode is allocated or freed.
4.3 The classical inode layout
The classical inode — the one introduced by the original Unix file system and refined by the Berkeley Fast File System — has a layout that is worth examining in detail, because the design has been influential and because the trade-offs it embodies are still relevant.
The inode has four groups of fields:
- Metadata. The file's type, mode, owner, group, size, timestamps, and link count. These fields are the ones that Lesson 10.2 examined. They occupy a fixed portion of the inode (typically the first 40 to 100 bytes, depending on the file system's version).
- Direct block pointers. A small number of pointers (typically 12 on the classical Unix file system) that point directly to the first data blocks of the file. For a small file — whose contents fit in the direct blocks — these pointers are all that is needed, and the file can be accessed with a single level of indirection.
- Indirect block pointers. Three additional pointers that point to blocks containing further pointers: the single indirect pointer points to a block of direct pointers, the double indirect pointer points to a block of single indirect blocks, and the triple indirect pointer points to a block of double indirect blocks. These pointers extend the file's maximum size without requiring the inode to hold an unbounded number of pointers.
- Extended attributes and other fields. Modern inodes include additional fields for extended attributes, checksums, and file-system-specific features.
4.4 The maximum file size
The inode's layout determines the maximum size of a file that the file system can hold. The maximum size is the sum of the blocks reachable through the direct pointers, the single indirect pointer, the double indirect pointer, and the triple indirect pointer. The calculation depends on the block size and on the size of a block pointer.
Let B be the block size (typically 4 KB on modern systems) and let P be the size of a block pointer (typically 4 bytes on 32-bit systems and 8 bytes on 64-bit systems). The number of pointers that fit in a block is B / P. Let N be this number — on a 4 KB block size with 4-byte pointers, N = 1024.
The number of blocks reachable through each pointer is:
- Direct: 12 blocks (assuming 12 direct pointers).
- Single indirect: N blocks.
- Double indirect: N2 blocks.
- Triple indirect: N3 blocks.
The total number of blocks that the inode can address is 12 + N + N2 + N3, and the maximum file size is that number multiplied by the block size B.
For the classical Unix file system with 4 KB blocks and 4-byte pointers (N = 1024), the maximum file size is:
blocks = 12 + 1024 + 1024² + 1024³
= 12 + 1024 + 1,048,576 + 1,073,741,824
= 1,074,791,436 blocks
max size = 1,074,791,436 × 4 KB
≈ 4.3 TB
The maximum is dominated by the triple indirect pointer, which alone accounts for 4 TB of the total. The direct pointers cover only 48 KB and the single indirect pointer only 4 MB, so a file that fits in 4 MB is accessed with at most one level of indirection, and a file that fits in 4 GB is accessed with at most two. This is the trade-off that makes the classical inode efficient: it uses the simplest possible addressing for the common case (small and medium files) and reserves the more expensive addressing for the rare case (very large files).
The maximum size is not unlimited. A file system with 4 KB blocks and 4-byte pointers cannot hold a file larger than about 4 TB, even with the triple indirect pointer. Modern file systems extend the limit by using larger block sizes, 8-byte pointers (which reduce N to 512 but allow more address space), or entirely different mechanisms — extents, B-trees, or copy-on-write trees — that do not have a fixed upper bound.
4.5 Accessing a block in the classical inode
To access a specific block of a file, the file system must follow the appropriate pointer chain from the inode. The number of disk accesses depends on which chain is used:
| Block offset | Pointer chain | Disk accesses (inode cached) |
|---|---|---|
| 0 to 11 | direct | 1 |
| 12 to 12 + 1023 | single indirect | 2 |
| 12 + 1024 to 12 + 1024 + 1024² | double indirect | 3 |
| beyond that | triple indirect | 4 |
The table assumes the inode itself is already in memory (which it usually is, because the file is open), and it counts only the disk accesses needed to read the pointer blocks and the data block. For a file whose block is reachable through the direct pointers, the file system can access the data with a single disk read: the inode is in memory, the pointer is in the inode, and the data block is fetched. For a block in the triple indirect range, the file system must read the triple indirect block, the double indirect block, the single indirect block, and the data block — four disk reads to access one block.
The extra disk accesses are the cost of the multi-level addressing. On a system with a mechanical disk, four reads are much more expensive than one; on an SSD, the difference is smaller but still significant. Modern file systems reduce the cost by using extents — a range of contiguous blocks with a single descriptor — which allows a single pointer to cover many blocks and eliminates the multi-level chains for most files. Extents are the subject of Lesson 10.5 and Lesson 10.7; here we note only that the classical inode's multi-level addressing is one of the design decisions that modern file systems have moved away from.
4.6 Inodes and path resolution
The inode plays a central role in path resolution, the process by which the kernel converts a path into a reference to a file (Lesson 10.1). The resolution processes each path component in turn, and at each step it reads the directory's data (which is a file with its own inode) to find the entry for the next component, then reads the inode that the entry points to.
The steps for a component name in a
directory D are:
-
Read the directory entry. The kernel
looks up
namein the directory's data (which it accesses throughD's inode, using the same multi-level addressing as any other file). The entry contains an inode number, say k. -
Locate the inode. The kernel computes
the location of inode k in the inode table.
The location is a simple function of the inode number
and the inode size: inode k is at offset
k ×
sizeof(inode)from the start of the inode table. - Read the inode. The kernel reads the inode from disk (if it is not already in the inode cache) and checks its type, its permissions, and whether it is a directory (if there are further components to process).
- Continue with the next component. If there are further components, the kernel uses the inode's data pointers to read the next directory and repeats from step 1.
At each step, the kernel performs one or more disk accesses: one to read the directory's data (through the directory's inode), one to read the inode's metadata, and potentially more if the inode's data spans multiple blocks. The directory entry cache (dcache) and the inode cache (icache) reduce the cost by holding recently used entries in memory: once a path has been resolved, the resolution of the same path (or a path that shares a prefix) is much faster, because the entries are already cached.
The dcache and the icache are the file system's equivalent of the TLB (Lesson 9.3) and the page cache (Lesson 9.4): they hold the results of recent lookups so that subsequent lookups can be served without disk access. On a busy system, the hit rate is very high, and the cost of path resolution is dominated by the cached case.
4.7 Inode lifetime and the link count
An inode's life begins when the file is created and ends when the file is deleted. The inode's lifetime is managed by the link count, the field that records how many hard links point to the inode. The rules are:
- Creation. When a new file is created, the file system allocates a free inode from the inode bitmap, initializes its fields (type, permissions, owner, timestamps), sets its link count to 1, and adds an entry to the containing directory that maps the file's name to the new inode's number.
-
Link. When a hard link is created
(
link, orlnwithout-s), the file system adds a new directory entry that maps the new name to the existing inode's number and increments the inode's link count. The inode itself is not modified in any other way; the file's contents and metadata are shared by both names. -
Unlink. When a name is removed
(
unlink, orrm), the file system removes the directory entry and decrements the inode's link count. If the count is now zero, the file system checks whether any process has the file open. If not, the file's data blocks are freed and the inode is marked free. If the count is still positive, the inode is not freed: the file is still reachable through other names. - Open files. If a process has the file open when the link count reaches zero, the inode is not freed until the process closes the file. This allows a program to delete a temporary file and continue to use it: the file's name is gone, but the file's contents are still accessible through the open file descriptor. The space is reclaimed when the last file descriptor referring to the file is closed.
The link count is what makes the Unix file model's handling of deletion intuitive. Deleting a file does not immediately destroy it; it removes one of the file's names. The file's contents persist as long as there is some way to reach them — another hard link, or an open file descriptor — and they are freed only when the last reference is gone. This is what allows multiple names for a file, and it is what makes the deletion operation cheap (it updates a few fields in the inode and the directory, rather than walking the file's data blocks).
4.8 The VFS inode cache
The inode on disk is not the same as the inode in memory.
The on-disk inode is a fixed-size record in the inode
table, optimized for compact storage; the in-memory inode
is a kernel data structure (the struct inode
in the Linux kernel) that holds the same information plus
additional fields used by the kernel: the file's
reference count, the device and file-system that own it,
pointers to the file's operations, and the state that
the kernel needs to manage the open file.
The Linux kernel's Virtual File System (VFS) is the layer that unifies the different file systems the kernel supports. Every file system implements a common interface — a set of operations for reading and writing inodes, directories, and data — and the VFS presents a single, uniform view of the file system to user space. The VFS is what allows a Linux system to mount an ext4 file system, an XFS file system, and a network file system side by side, and to access them all through the same system calls.
The VFS maintains an inode cache (the
icache): a hash table of in-memory inodes keyed
by the pair (device, inode number). When the
kernel needs an inode, it first checks the cache; if the
inode is present, the kernel uses the cached copy; if it
is not, the kernel reads the on-disk inode, constructs an
in-memory inode, and inserts it into the cache. The
cache is the reason a subsequent access to a file is
faster than the first: the inode is already in memory,
and the kernel does not need to read it from disk again.
The inode cache is bounded by a tunable limit, and when the limit is reached, the kernel evicts inodes that are not currently in use. An inode is in use if at least one open file descriptor refers to it, or if it is a directory that is currently being traversed; the kernel does not evict in-use inodes, because doing so would invalidate the references that the open files hold. The eviction policy is a variant of LRU: inodes that have not been accessed recently are the first candidates for eviction.
The inode cache is what makes the file system's operations fast in the common case. On a busy system, the cache hit rate is very high, and the cost of most file operations is dominated by the memory access rather than by the disk access. The same principle applies as for the TLB and the page cache: the file system's performance is determined by the locality of the workload, and the caches are what turn that locality into speed.
4.9 Inode exhaustion
The classical file system's inode table is fixed in size: the number of inodes is chosen when the file system is created and does not change. This creates a problem that is easy to overlook: a file system can run out of inodes while it still has plenty of free data blocks. When the inode table is exhausted, the file system cannot create new files, even if it has gigabytes of free space.
The problem is well-known in practice. A mail spool, a
source-code repository, a web cache, and a directory of
small images are all workloads that create many small
files and consume inodes rapidly. On a traditional Unix
file system with a fixed inode table, such a workload can
exhaust the inodes long before the data blocks. The
symptom is confusing: df reports plenty of
free space, but every attempt to create a new file fails
with ENOSPC (no space left on device).
The classic remedies are to increase the inode count (by reformatting the file system with a larger table, which is destructive), to move the workload to a file system with more inodes, or to change the workload's structure (for example, by storing many small items in a database rather than as individual files). The modern remedy is to use a file system whose inode count is not fixed: ext4 allocates inodes dynamically (within the constraints of its block groups), and Btrfs and ZFS allocate metadata on demand, with no inode table at all.
Inode exhaustion is a useful illustration of a general principle: the fixed partitioning of a resource into separate pools is a design decision that has consequences. The classical file system separates the inode table (a fixed pool of metadata) from the data block area (a fixed pool of data), and the two pools can be exhausted independently. Modern file systems blur the distinction by allocating metadata from the same free space as data, which makes the resource more flexible but which also makes the file system's management more complex. The trade-off is between simplicity and flexibility, and the modern file systems have chosen flexibility.
4.10 Modern inode structures
Modern file systems have moved away from the classical inode's fixed layout and multi-level pointers, but they retain the inode as a concept: a per-file structure that holds the file's metadata and locates its data. The differences are in how the structure is stored and how the file's data is located.
- ext4
- The default Linux file system for many years. Ext4 uses a variable-size inode (typically 256 bytes) and supports two allocation schemes: the classical multi-level pointers (for compatibility with ext2 and ext3) and extents (for new files). An extent is a descriptor of a range of contiguous blocks; a single extent can cover many blocks, which is much more efficient than a single pointer per block. Ext4 also supports delayed allocation, which allows the file system to allocate blocks in larger runs, and it supports extents for the inode's data, so the multi-level pointer chains are used less often.
- XFS
- A high-performance file system originally developed by SGI. XFS uses a variable-size inode stored in a B-tree, and it uses extents for data allocation. The B-tree structure allows the inode to grow as needed: a file with many extents has a larger inode than a file with few, and the file system allocates metadata blocks to hold the additional extents. The result is that XFS has no fixed upper bound on the number of extents a file can have, and it can support very large files and very fragmented files efficiently.
- Btrfs
- A modern copy-on-write file system for Linux. Btrfs does not have a separate inode structure: the file's metadata is stored in a B-tree (the fs tree), and the tree is updated atomically by copy-on-write. When a file is modified, the B-tree nodes on the path from the root to the modified leaf are copied and updated, and the new versions are written to new locations on disk. The old versions remain until the tree is committed, which gives Btrfs its crash-consistency guarantees and its snapshot capability. The absence of a separate inode table means that Btrfs cannot run out of inodes independently of its data space: both are allocated from the same free space.
- ZFS
- A file system originally developed by Sun Microsystems and now maintained by the OpenZFS project. Like Btrfs, ZFS uses copy-on-write and a tree of metadata blocks, and it has no separate inode table. A ZFS file is represented by a dnode, a structure analogous to an inode but stored within the file system's metadata tree. The dnode holds the file's attributes and the pointers to its data blocks, and it can be updated, moved, and copied by the file system's copy-on-write machinery. ZFS also provides checksums on all data and metadata, and it uses RAID-Z for redundancy, so it is significantly more complex than the classical inode but also more reliable.
The common trend across modern file systems is a move away from the fixed-size, fixed-location, multi-level inode and toward a variable-size, tree-based structure that can grow as needed and that is updated by copy-on-write. The change gives the file systems more flexibility and better crash-consistency, at the cost of more complex internal structures and more metadata overhead. The inode as a concept — a per-file metadata structure that identifies the file and locates its data — remains the same.
4.11 Common misconceptions
"An inode contains the file's name."
The name is stored in the directory, not in the inode. The inode holds the file's identity (its inode number), its metadata, and its data pointers, but not its name.
"An inode contains the file's data."
The inode contains pointers to the file's data, not the data itself. The data is stored in separate blocks on the disk, which the inode's pointers locate.
"Every file system has inodes."
The concept of a per-file metadata structure is universal, but the structure is not always called an inode. ZFS uses a dnode; Btrfs stores metadata in B-trees; the classical Unix file system uses the inode. The name is a convention, not a requirement.
"The inode number is the file's location on disk."
The inode number is the file's identity within the file system, not its location. It is an index into the inode table, and from the index the file system computes the inode's location. The file's data blocks are located through the inode's pointers, not through the inode number.
"A file with a large logical size always uses many data blocks."
Not necessarily. A sparse file (Lesson 10.2) can have a large logical size and few data blocks, because the holes in the file are not allocated.
"The number of inodes is unlimited."
On classical file systems, the number of inodes is fixed at creation time, and a file system can run out of inodes while it still has free data blocks. On modern file systems (ext4, Btrfs, ZFS), inodes are allocated dynamically and the limit is much less restrictive.
"The classical inode's pointer scheme is obsolete."
The classical inode's multi-level pointer scheme has been largely replaced by extents in modern file systems, but the inode concept — a per-file structure that holds metadata and locates data — remains, and the classical scheme is still used on files that were created by older versions of ext4 or its predecessors.
"The kernel reads the inode from disk on every access."
The kernel maintains an inode cache (the icache) that holds recently used inodes in memory. On a busy system, the cache hit rate is very high, and most accesses are served from the cache without any disk access.
05 Worked example
The following example computes the maximum file size of a classical inode, traces the disk accesses for three different block offsets, and demonstrates the inode's lifetime across a sequence of operations.
Maximum file size for a classical inode
Consider a classical Unix file system with a 4 KB block size and 4-byte block pointers. The inode has 12 direct pointers, and one pointer each for the single, double, and triple indirect blocks. We compute the maximum file size.
The number of pointers that fit in a block is:
N = block_size / pointer_size
= 4096 / 4
= 1024
The number of blocks reachable through each pointer class is:
| Pointer class | Blocks reachable | Bytes reachable |
|---|---|---|
| Direct (12 pointers) | 12 | 48 KB |
| Single indirect | 1,024 | 4 MB |
| Double indirect | 1,048,576 | 4 GB |
| Triple indirect | 1,073,741,824 | 4 TB |
| Total | 1,074,791,436 | ≈ 4.3 TB |
The maximum file size is dominated by the triple indirect pointer, which alone accounts for 4 TB of the total. The direct pointers cover only 48 KB, and the single indirect pointer only 4 MB. This is the reason the classical inode is efficient for typical files: a file that fits in 48 KB uses no indirect blocks at all, a file that fits in 4 MB uses at most one, and only files larger than 4 MB need the double indirect pointer.
Now consider the same file system but with 8-byte block pointers (as on a modern 64-bit system). The number of pointers per block is halved (N = 512), and the maximum file size changes:
| Pointer class | Blocks reachable (N=512) | Bytes reachable |
|---|---|---|
| Direct (12 pointers) | 12 | 48 KB |
| Single indirect | 512 | 2 MB |
| Double indirect | 262,144 | 1 GB |
| Triple indirect | 134,217,728 | 512 GB |
| Total | 134,480,396 | ≈ 513 GB |
The maximum file size drops from 4.3 TB to 513 GB because each pointer is larger and fewer pointers fit in a block. This is a real constraint: the classical Unix file system's 4.3 TB limit assumes 32-bit pointers, and a 64-bit system with the same inode layout would have a much smaller maximum. Modern file systems address this by using extents (which cover many blocks with a single descriptor) rather than indirect pointers.
Accessing a block through the inode
Consider the same file system with 4 KB blocks and 4-byte pointers. We trace the disk accesses needed to read three specific blocks of a file, assuming the inode is already in memory (because the file is open):
| Block offset | Chain | Disk accesses | What is read at each access |
|---|---|---|---|
| 5 | direct | 1 | data block 5 (through direct[5]) |
| 500 | single indirect | 2 | 1. the single indirect block; 2. the data block |
| 500,000 | double indirect | 3 | 1. the double indirect block; 2. the single indirect block; 3. the data block |
| 500,000,000 | triple indirect | 4 | 1. the triple indirect block; 2. the double indirect block; 3. the single indirect block; 4. the data block |
The pattern is clear. Accessing a block in the direct range costs one disk access, in the single-indirect range two, in the double-indirect range three, and in the triple-indirect range four. The extra accesses are the price of the multi-level addressing, and they are why the classical inode's performance degrades as the file's offset increases.
On a modern file system with extents, the calculation is different. A file with a single extent (its blocks are contiguous on disk) can be accessed with one or two disk accesses regardless of the block's offset, because the extent descriptor in the inode specifies the range of blocks directly. Only files with many extents (because they are fragmented) need additional accesses, and even then the number is bounded by the number of extents, not by the file's size.
Inode lifetime across a sequence of operations
We trace the inode's state through a sequence of operations on a file.
| Step | Operation | Link count | Open count | Inode state |
|---|---|---|---|---|
| 0 | — | 0 | 0 | free (in the free bitmap) |
| 1 | open("file", O_CREAT) |
1 | 1 | allocated; directory entry added |
| 2 | write(fd, ..., n) |
1 | 1 | data written; size, mtime, ctime updated |
| 3 | link("file", "alias") |
2 | 1 | new directory entry; ctime updated |
| 4 | unlink("file") |
1 | 1 | directory entry removed; ctime updated |
| 5 | close(fd) |
1 | 0 | still allocated (link count 1) |
| 6 | unlink("alias") |
0 | 0 | freed; data blocks released |
The trace shows the interplay of the link count and the open count. The inode is allocated as long as either count is positive; it is freed only when both are zero. At step 4, the file's original name is removed, but the inode is not freed because the link count is still positive (through the alias) and the open count is positive (the file is still open). At step 6, the last link is removed and the file is no longer open, so the inode is freed and the data blocks are released.
A different sequence illustrates the "delete an open file" pattern that is common in Unix:
| Step | Operation | Link count | Open count | Inode state |
|---|---|---|---|---|
| 1 | fd = open("temp", O_CREAT) | 1 | 1 | allocated |
| 2 | unlink("temp") | 0 | 1 | not freed: file is open |
| 3 | write(fd, ..., n) | 0 | 1 | still accessible via fd |
| 4 | read(fd, ..., n) | 0 | 1 | still accessible via fd |
| 5 | close(fd) | 0 | 0 | freed; data blocks released |
This is the Unix idiom for a temporary file: create a
file, open it, unlink it immediately, and use the open
file descriptor for the rest of the program's life. The
file has no name, but it has an open file descriptor,
and the inode is kept alive by the descriptor. When the
program closes the descriptor (or exits, which closes
all descriptors automatically), the file's space is
reclaimed. The idiom is used by tmpfile()
in C, by the O_TMPFILE flag in Linux, and
by many applications that need a scratch file.
Demonstrating inode exhaustion
Inode exhaustion is easy to demonstrate on a small
file system. Create a small file system with a limited
number of inodes, mount it, and create many small
files until the inode table is exhausted. The
df -i command reports the inode usage.
# Create a 20 MB file system with a small inode count
dd if=/dev/zero of=/tmp/small.img bs=1M count=20
mkfs.ext4 -N 1000 /tmp/small.img # 1000 inodes
# Mount it
sudo mkdir /mnt/small
sudo mount -o loop /tmp/small.img /mnt/small
# Check the inode usage
df -i /mnt/small
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/loop0 1000 11 989 2% /mnt/small
# Create files until the inode table is exhausted
cd /mnt/small
for i in $(seq 1 1000); do
touch file$i 2>&1 | head -1
done
# Check the inode usage again
df -i /mnt/small
df -h /mnt/small # data blocks are still free
# Clean up
cd /
sudo umount /mnt/small
rm /tmp/small.img
After creating the files, df -i shows
that the inode usage is at or near 100 percent, while
df -h shows that plenty of data blocks
are still free. The next attempt to create a file fails
with ENOSPC, even though the file system
has free space. This is the inode exhaustion problem in
action.
The failure message is the same as the one for running
out of data blocks, which is why inode exhaustion is
sometimes confusing: the user sees "no space left on
device" and checks the free space, which is fine. The
remedy is to check df -i as well as
df -h.
06 Interactive activity
Activity · explore inodes on a live system
The goal of this activity is to use the Linux command line to observe inodes in action, to examine their layout, and to demonstrate the inode's role in file identity and lifetime. Work through the steps in your course VM.
-
Observe inode numbers.
Use
ls -iandstatto see the inode number of files, and confirm that the number identifies the file regardless of its name.
Create a file, note its inode number, rename it, and confirm that the inode number is unchanged.ls -li /etc/passwd ls -li /etc/passwd /etc/shadow stat -c 'inode=%i links=%h name=%n' /etc/passwd /bin/ls /tmptouch /tmp/inode-test.txt stat -c 'inode=%i name=%n' /tmp/inode-test.txt mv /tmp/inode-test.txt /tmp/inode-test-renamed.txt stat -c 'inode=%i name=%n' /tmp/inode-test-renamed.txt -
Examine the inode table.
Use
df -iandtune2fs -lto see the inode table's size, usage, and free count.
Thedf -i / df -i /tmp sudo tune2fs -l /dev/sda1 2>/dev/null | grep -i inode sudo dumpe2fs /dev/sda1 2>/dev/null | grep -i inode | headdf -ioutput reports the total inodes, the used inodes, the free inodes, and the percentage used. Thetune2fsoutput reports additional file-system parameters, including the inode size, the inodes per group, and the first inode. -
Examine the inode's metadata.
Use
statto see all the metadata that the inode holds, and identify each field.
For each file, identify: its inode number, its size, its block count, its mode, its owner, its link count, and its three timestamps. Note that thestat /etc/passwd stat /bin/ls stat /tmpstatoutput is the user-space view of the inode's contents. -
Demonstrate the link count.
Create a file, create a hard link to it, and observe
how the link count changes.
Observe that both names have the same inode number, that creating the link increments the count, and that removing one name decrements it without deleting the file (the file is still accessible through the other name).echo "hello" > /tmp/link-demo.txt stat -c 'inode=%i links=%h' /tmp/link-demo.txt ln /tmp/link-demo.txt /tmp/link-demo-alias.txt stat -c 'inode=%i links=%h' /tmp/link-demo.txt rm /tmp/link-demo.txt stat -c 'inode=%i links=%h' /tmp/link-demo-alias.txt rm /tmp/link-demo-alias.txt -
Demonstrate the "delete an open file"
pattern.
Write a program that creates a temporary file, unlinks
it, and continues to use it through the open file
descriptor.
Compile and run the program. Observe that the file is gone from the directory, but the data is still accessible through the file descriptor. When the program closes the descriptor, the inode is freed and the space is reclaimed.#include <fcntl.h> #include <stdio.h> #include <unistd.h> int main(void) { int fd = open("tempfile", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) { perror("open"); return 1; } /* write some data */ write(fd, "hello, world\n", 13); /* unlink the file — but the inode is still alive */ if (unlink("tempfile") == -1) { perror("unlink"); return 1; } /* the file is gone from the directory but still accessible */ system("ls -l tempfile 2>&1"); /* seek back to the beginning and read the data */ lseek(fd, 0, SEEK_SET); char buf[32]; ssize_t n = read(fd, buf, sizeof(buf) - 1); buf[n] = '\0'; printf("read: %s", buf); /* close the file — now the inode is freed */ close(fd); return 0; } -
Demonstrate inode exhaustion.
Create a small file system with a limited number of
inodes, mount it, and create files until the inode
table is exhausted. Observe the error messages.
After the loop,# Create a 20 MB file system with 1000 inodes dd if=/dev/zero of=/tmp/small.img bs=1M count=20 mkfs.ext4 -N 1000 /tmp/small.img # Mount it sudo mkdir -p /mnt/small sudo mount -o loop /tmp/small.img /mnt/small # Check the inode usage df -i /mnt/small df -h /mnt/small # Create files until the inode table is exhausted cd /mnt/small for i in $(seq 1 1100); do touch file$i 2>/dev/null || { echo "failed at file$i"; break; } done # Check the inode usage again df -i /mnt/small df -h /mnt/small # Clean up cd / sudo umount /mnt/small rm /tmp/small.imgdf -ishould show nearly 100 percent inode usage whiledf -hshows plenty of free space. The error message on the failed creation is "No space left on device", which is confusing given the free space — this is the inode exhaustion problem. -
Observe the inode cache.
The Linux kernel exposes the inode cache through the
/proc/sys/fs/inode-*files.
Thecat /proc/sys/fs/inode-nr cat /proc/sys/fs/inode-state cat /proc/sys/fs/inode-max 2>/dev/nullinode-nrfile reports the number of inodes currently allocated; theinode-statefile reports additional information, including the number of free inodes (that the kernel can evict) and the number of in-use inodes. Run a workload that opens many files and observe the counters. -
Read the man pages.
Read the man pages that document the inode-related
interfaces.
For each, write a sentence describing its main contribution to your understanding of the inode. Theman 2 stat man 2 link man 2 unlink man 2 rename man 8 tune2fs man 8 dumpe2fs man 1 dfman 2 linkandman 2 unlinkpages document the operations that change the link count; theman 8 tune2fsandman 8 dumpe2fspages document the tools for inspecting the inode table; and theman 1 dfpage documents the tool for checking inode usage.
Reflect: the inode is one of those structures that is simple enough to be understood in a few minutes but whose consequences are subtle enough to take years to appreciate. The separation of the inode from the directory, the multi-level pointer scheme, the link count, the in-memory cache, and the fixed size of the inode table — each of these design decisions has consequences that reach deep into the system's behaviour. Which of them do you think is the most important? Which would you change if you were designing a file system today? And what would be the cost of the change?
07 Knowledge check
Check your understanding
Question 1
Define an inode and describe its three roles in the file system. What is the inode table, and what is the inode number?
Show answer
struct stat
interface exposes. (3) Data map.
The inode holds the pointers to the file's data
blocks: the direct pointers, and the indirect
pointers (single, double, and triple) that extend
the file's maximum size. The
inode table is the file system's
array of inodes. It is a contiguous region of the
disk whose size is determined when the file system
is created, and each inode is a fixed-size record
within it. The inode number is
the index into the inode table: inode k is
at position k in the table, so the file
system can locate any inode by its number with a
single computation. The number of inodes is fixed
at file-system creation time on classical file
systems, which means the file system can hold at
most as many files as it has inodes, regardless of
the amount of free data blocks. Modern file systems
(ext4, Btrfs, ZFS) relax this constraint by
allocating inodes dynamically.
Question 2
Describe the classical inode's layout, including the direct and indirect pointers. Compute the maximum file size for a file system with a 4 KB block size and 4-byte pointers.
Show answer
4096 / 4 = 1024. The
blocks reachable through each pointer class are:
- Direct: 12 blocks = 48 KB
- Single indirect: 1024 blocks = 4 MB
- Double indirect: 1024² = 1,048,576 blocks = 4 GB
- Triple indirect: 1024³ = 1,073,741,824 blocks = 4 TB
Question 3
Explain the inode's lifetime and the role of the link count. What happens when a file is unlinked while it is still open by a process?
Show answer
st_nlink)
is the number of hard links that point to the
inode, and it governs the inode's lifetime. When
the file is created, the file system allocates a
free inode, initializes its fields, sets the link
count to 1, and adds a directory entry that maps
the file's name to the inode's number. When a hard
link is created, the file system adds a new
directory entry and increments the link count; the
inode itself is not otherwise modified, so both
names refer to the same file. When a name is
removed (unlink), the file system
removes the directory entry and decrements the
link count. If the count is now zero, the file
system checks whether any process has the file
open. If not, the file's data blocks are freed and
the inode is marked free. If the count is still
positive, the inode is not freed: the file is
still reachable through other names. If a process
has the file open when the link count reaches
zero, the inode is not freed until the process
closes the file. This is the "delete an open file"
pattern, and it is used for temporary files:
fd = open("temp", O_RDWR | O_CREAT);
unlink("temp"); /* the file is gone from the directory */
write(fd, ...); /* but the data is still accessible */
read(fd, ...); /* through the file descriptor */
close(fd); /* now the inode is freed */
The idiom is supported by the kernel because the
inode is kept alive by the open file descriptor
even after the link count reaches zero. This is
what makes temporary files easy to manage: the
application does not need to remember to delete
them, because they are already gone from the
directory. When the program closes the file (or
exits, which closes all descriptors), the space
is reclaimed.
Question 4
What is the VFS inode cache? How does it relate to the on-disk inode, and what is the difference between the on-disk inode and the in-memory inode?
Show answer
(device, inode number), and it
holds the in-memory inodes that the kernel has
recently used. When the kernel needs an inode, it
first checks the cache; if the inode is present,
the kernel uses the cached copy; if it is not, the
kernel reads the on-disk inode, constructs an
in-memory inode, and inserts it into the cache.
The cache is bounded by a tunable limit, and when
the limit is reached, the kernel evicts inodes
that are not currently in use (an inode is in use
if at least one open file descriptor refers to it,
or if it is a directory being traversed). The
relationship between the on-disk inode and the
in-memory inode is that the latter is a richer,
kernel-specific structure that holds the same
information as the former plus additional fields:
the inode's reference count, the device and
file-system that own it, pointers to the file's
operations (the file system's inode operations
table), and the state that the kernel needs to
manage the open file. The on-disk
inode is a fixed-size record in the inode
table, optimized for compact storage. The
in-memory inode is a variable-size
structure (the struct inode) that
exists only while the file is in use or cached, and
is discarded when the file is closed and the
cache's space is needed. The distinction matters
because the kernel's operations on the inode (such
as the permission check and the data-block
lookup) work on the in-memory inode, and the
on-disk inode is read and written only when the
in-memory inode is loaded or flushed. The VFS is
what allows the kernel to support many different
file systems through the same interface: each file
system provides its own functions for reading and
writing its inodes, and the VFS presents a
uniform view to the rest of the kernel.
Question 5
What is inode exhaustion? Why does the classical file system have this problem, and how do modern file systems address it?
Show answer
df -h
reports plenty of free space, but every attempt to
create a new file fails with ENOSPC
("no space left on device"). The error message is
the same as the one for running out of data
blocks, so the user must check df -i
as well as df -h to diagnose the
problem. The classical remedies are to increase
the inode count (which requires reformatting the
file system, a destructive operation), to move the
workload to a different file system, or to change
the workload's structure (for example, by storing
many small items in a database rather than as
individual files). Modern file systems address the
problem in two ways. First, some file systems
(ext4) allocate inodes dynamically from within
their block groups, so the inode count is not
fixed at creation and can grow as needed (within
the constraints of the file system's layout).
Second, some file systems (Btrfs, ZFS) have no
separate inode table at all: their metadata is
stored in trees (B-trees or copy-on-write trees),
and the metadata blocks are allocated from the
same free space as data. The distinction between
"metadata space" and "data space" is therefore
blurred, and the file system cannot run out of one
independently of the other. The change gives the
file system more flexibility at the cost of more
complex internal structures.
08 Unit discussion
Discussion prompt
The inode is one of the most influential data structures in the history of computing. It was designed for the original Unix file system in the early 1970s, refined by the Berkeley Fast File System in the 1980s, and it has been adapted and extended by every Unix-like file system since. Its design decisions — the separation of the inode from the directory, the fixed size of the inode record, the multi-level pointer scheme, the link count as the file's lifetime reference — have shaped the way file systems work for five decades.
But the inode is not a perfect design. Its fixed size limits the number of pointers it can hold, which limits the maximum file size and the number of extents a file can have. Its fixed location in the inode table means that the inode's position is determined by its number, which makes it hard to allocate inodes in a way that matches the file's data layout. And its fixed count in the inode table means that a file system can run out of inodes before it runs out of data, which is a well-known operational problem.
Modern file systems have moved away from the classical inode's fixed structure in several directions: variable- size inodes (XFS), extents instead of indirect pointers (ext4, XFS), B-tree-based metadata (XFS, Btrfs), and copy-on-write trees (Btrfs, ZFS). Each of these changes addresses one or more of the classical inode's limitations, at the cost of more complex internal structures and more metadata overhead.
Which of the classical inode's design decisions do you think is the most worth preserving, and which is the most worth changing? Is there a fundamental reason that file systems still have a per-file metadata structure, or is the inode a historical accident that future systems could do without? Consider the cloud storage services that have displaced traditional file systems for many workloads: they typically have a much simpler metadata model, and they sacrifice the classical inode's richness for scalability and simplicity. Is the classical inode the right structure for the next decade? Post your response to the Unit 10 discussion forum and reply to at least two classmates. Supports CLO1, CLO5, and CLO6.
09 Practice exercise
This ungraded exercise consolidates the lesson and prepares you for Lesson 10.4. Aim to spend 60–90 minutes on it.
-
Compute the maximum file size.
For each of the following file-system configurations,
compute the maximum file size supported by a classical
inode with 12 direct pointers and the three indirect
pointers:
- 1 KB blocks, 4-byte pointers
- 2 KB blocks, 4-byte pointers
- 4 KB blocks, 4-byte pointers
- 4 KB blocks, 8-byte pointers
- 8 KB blocks, 8-byte pointers
- 16 KB blocks, 8-byte pointers
-
Trace a block access.
For a file system with a 4 KB block size and 4-byte
pointers (12 direct pointers), state which pointer
chain is used to access each of the following block
offsets, and how many disk accesses are needed (not
counting the inode itself):
- 0
- 11
- 12
- 1023
- 1035
- 1,048,588
- 1,073,742,000
-
Trace the inode's lifetime.
A program performs the following operations on a
file. Trace the inode's link count and open count
after each step, and state whether the inode is
allocated or free.
open("f", O_CREAT)→ fd1link("f", "g")open("g", O_RDONLY)→ fd2unlink("f")close(fd1)unlink("g")close(fd2)
unlink("g")had been performed before theclose(fd1)? -
Demonstrate the delete-an-open-file
pattern.
Write a C program that creates a temporary file,
unlinks it, writes to it, seeks back, reads the data
back, and then closes it. Verify with
lsthat the file is gone from the directory while it is open. What does/proc/<pid>/fd/show for the open file descriptor?
The#include <fcntl.h> #include <stdio.h> #include <unistd.h> int main(void) { int fd = open("tempfile", O_RDWR | O_CREAT | O_TRUNC, 0600); unlink("tempfile"); write(fd, "hello, world\n", 13); /* Observe the file descriptor */ char path[64]; snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd); char target[256]; ssize_t n = readlink(path, target, sizeof(target) - 1); if (n > 0) { target[n] = '\0'; printf("fd → %s\n", target); } lseek(fd, 0, SEEK_SET); char buf[32]; n = read(fd, buf, sizeof(buf) - 1); buf[n] = '\0'; printf("read: %s", buf); close(fd); return 0; }readlinkon/proc/<pid>/fd/<fd>will show something like/path/to/tempfile (deleted)— the file's name is gone but the inode is still alive. -
Demonstrate inode exhaustion.
Follow the steps in the worked example to create a
small file system with a limited number of inodes,
mount it, and exhaust the inode table. Then compute
the number of inodes per megabyte of storage, and
compare it to the number on your root file system
(using
tune2fs -landdf -h). What is the inodes-per-byte ratio? Which workload would exhaust the file system first: many small files or a few large files? - Compare inode structures. Write a short paragraph (200–250 words) comparing the classical Unix inode with the modern structures used by ext4, XFS, Btrfs, and ZFS. Your paragraph should address: (a) the size and location of the metadata structure; (b) how the file's data blocks are located (indirect pointers, extents, B-trees, copy-on-write trees); (c) whether the file system has a fixed inode table; (d) the trade-offs of each design.
-
Investigate the VFS inode cache.
On Linux, the kernel's inode cache statistics are
available in
/proc/sys/fs/inode-*. Run a workload that opens many files (for example, a recursivefindover the file system), and observe how the counters change.
What doescat /proc/sys/fs/inode-nr find /usr -type f 2>/dev/null | head -10000 cat /proc/sys/fs/inode-nr cat /proc/sys/fs/inode-stateinode-nrreport? What is the difference between the first number and the second number ininode-state? How does the cache behave after a large number of file accesses? - Reflect. In your own words, explain why the classical inode's pointer scheme is a trade-off. What does it optimize for, and what does it sacrifice? Then describe one situation in your own experience — a program you have written or a system you have used — where the inode's design mattered. What was the situation, and what did the inode's design make easy or hard?
10 Summary
-
An inode is a fixed-size data structure
that contains all the information the file system knows
about a file, except the file's name. It has three
roles: it is the file's identity (its
number identifies the file within the file system), it
holds the file's metadata (the fields
that
struct statexposes), and it provides the data map (the pointers to the file's data blocks). - The inode table is the file system's array of inodes. It is a contiguous region of the disk whose size is fixed at file-system creation time, and the inode number is the index into the table. The classical file system's inode table is fixed in size, which creates the inode exhaustion problem.
- The classical inode's layout consists of metadata fields, 12 direct block pointers, one single indirect pointer, one double indirect pointer, and one triple indirect pointer. The direct pointers cover the first 12 blocks of the file with no indirection; the indirect pointers cover the rest with one, two, or three levels of pointer blocks.
- The maximum file size supported by the classical inode is the sum of the blocks reachable through the four pointer classes. For a 4 KB block size and 4-byte pointers, it is approximately 4.3 TB. The maximum is dominated by the triple indirect pointer; the direct pointers cover only 48 KB.
- Accessing a block of a file requires 1 to 4 disk accesses depending on the block's offset: 1 for a direct block, 2 for a single-indirect block, 3 for a double-indirect block, and 4 for a triple-indirect block. The extra accesses are the price of the multi-level addressing.
- The inode plays a central role in path resolution: at each step, the kernel reads the directory entry (through the directory's inode), obtains the inode number, and reads the inode. The directory entry cache (dcache) and the inode cache (icache) reduce the cost of repeated resolutions.
- The inode's lifetime is governed by the link count and the open count. An inode is allocated as long as either count is positive, and it is freed only when both reach zero. The "delete an open file" pattern uses this behaviour for temporary files.
-
The VFS inode cache (the icache)
is the Linux kernel's cache of in-memory inodes, keyed
by the pair
(device, inode number). The on-disk inode is a fixed-size record in the inode table; the in-memory inode is a richer structure that holds the same information plus the kernel's own state. - Inode exhaustion is the situation in which a file system runs out of inodes while it still has free data blocks. It is a consequence of the classical file system's fixed inode table. Modern file systems (ext4, Btrfs, ZFS) address it by allocating metadata dynamically.
- Modern inode structures have moved away from the classical inode's fixed layout and multi-level pointers. Ext4 uses variable-size inodes and extents. XFS uses a B-tree-based inode with extents. Btrfs and ZFS use copy-on-write trees with no separate inode table at all. The inode as a concept — a per-file structure that holds metadata and locates data — remains.
Looking ahead
This lesson presented the inode: its role, its classical layout, its lifetime, its relationship to the VFS inode cache, and the modern inode structures that have replaced it. It referred several times to the directory as the structure that maps names to inode numbers, but did not examine the directory's internal structure in detail. Lesson 10.4 presents directory organization: how a directory's entries are stored, how they are searched, how they scale to millions of entries, and the data structures — linear lists, hash tables, B-trees — that modern file systems use to make directory operations fast. The lesson follows directly from the inode material, because the directory entries are what connect names to inodes.
11 References
- Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed., Chapter 14, Section 14.2.
-
Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C.
Operating Systems: Three Easy Pieces, Chapters 40
and 41. Available online at
ostep.org. - Tanenbaum, A. S., & Bos, H. Modern Operating Systems, 4th ed., Chapter 4, Section 4.3.
- McKusick, M. K., Joy, W. N., Leffler, S. J., & Fabry, R. S. A Fast File System for UNIX. ACM Transactions on Computer Systems, 1984. The paper that refined the classical inode and introduced cylinder groups.
- Bach, M. J. The Design of the UNIX Operating System. Prentice Hall, 1986. Chapters 4 and 5. The classical reference for the System V inode, the inode table, and the file-system operations.
- Ritchie, D. M., & Thompson, K. The UNIX Time-Sharing System. Communications of the ACM, 1974. The original paper that established the inode and the Unix file model.
- Kerrisk, M. The Linux Programming Interface. No Starch Press, 2010. Chapters 14 and 15.
- Love, R. Linux Kernel Development, 3rd ed., Chapter 13, The Virtual File System. The VFS inode, the inode cache, and the distinction between the on-disk and in-memory inodes.
- Bovet, D. P., & Cesati, M. Understanding the Linux Kernel, 3rd ed. O'Reilly, 2005. Chapters 12 and 18. The VFS inode and the Ext2/Ext3 inode.
-
Linux man-pages:
man 2 stat,man 2 statx,man 2 link,man 2 unlink,man 2 rename,man 2 open,man 5 proc,man 8 tune2fs,man 8 dumpe2fs,man 1 df.