Unlocking Linux: How the VFS and Filesystem Layers Work Together
This article explains Linux’s layered filesystem architecture, detailing how generic API calls like read interact with the VFS, superblock, inode, dentry, and buffer cache, and demonstrates mounting a filesystem using loop devices, dd, losetup, mke2fs, and mount commands.
Linux filesystem architecture abstracts complex storage systems using a set of generic API functions; for example, the read system call reads bytes from a file descriptor without knowing the filesystem type (ext3, NFS, etc.) or the underlying storage media (ATAPI, SAS, SATA).
When read is invoked on a file, the data is returned correctly through the layered implementation described below.
What is a filesystem?
A filesystem is a mechanism that organizes data and metadata on a storage device. Because many filesystem types and media exist, Linux implements a layered architecture that separates the user‑space interface, filesystem implementations, and device drivers.
Mount
In Linux, associating a filesystem with a storage device is called mount . The mount command attaches a filesystem to the current directory hierarchy at a mount point, requiring the filesystem type, the device, and the mount point.
To illustrate the process, a 10 MiB file is created with dd (using /dev/zero as the source).
The file is then associated with a loop device using losetup, making it appear as a block device ( /dev/loop0).
A filesystem is created on the loop device with mke2fs, producing a new ext2 filesystem.
The filesystem is then mounted with mount (e.g., mount -t ext2 /dev/loop0 /mnt/point1), after which standard commands like ls can be used on the new mount point.
The process can be repeated to create nested filesystems on top of the previously mounted one.
Filesystem Architecture
Linux treats all filesystems as a set of generic objects: superblock, inode, dentry, and file. The superblock resides at the root of each filesystem and maintains its state. Each object (file or directory) is represented by an inode, which stores all necessary metadata and supported operations.
High‑level Architecture
The high‑level diagram (Figure 1) shows the relationship between user‑space components (applications, glibc) and kernel components (VFS, filesystem implementations, block device drivers). User‑space issues system calls (open, read, write, close) that are routed by the system‑call interface to the appropriate kernel endpoints.
Figure 1. Linux filesystem component architecture
The VFS (Virtual Filesystem Switch) is the core interface that records supported filesystems and currently mounted filesystems. It provides two caches: the inode cache and the dentry cache, which store recently used inodes and directory entries respectively.
Each filesystem implementation (e.g., ext2, JFS) exports a set of generic interfaces used by VFS. The buffer cache sits below the filesystem implementations, caching read/write requests between the filesystem and the physical block device to improve performance. The sync command forces buffered data to be written to the underlying storage.
Main Structures
The superblock structure represents a filesystem, containing its name (e.g., ext2), size, status, block‑device reference, and metadata such as free block lists. Superblock operations (e.g., alloc_inode, destroy_inode, read_inode, write_inode, sync_fs) manage inodes and synchronize the filesystem.
Virtual Filesystem Layer
The VFS records the list of supported filesystems and the list of mounted filesystems (see Figure 3). New filesystems can be added at runtime with register_filesystem, which registers a file_system_type structure containing the filesystem’s name, attributes, and superblock callbacks. The current list can be viewed via cat /proc/filesystems.
Figure 2. Filesystems registered in the kernel
Figure 3. Mounted filesystem list
Superblock
The superblock contains essential information about a filesystem, such as its type, size, status, block‑device reference, and metadata like free block lists. It is usually stored on the storage medium but can be generated on the fly if absent.
inode and dentry
An inode uniquely identifies an object in a filesystem. Filesystem implementations provide methods to map filenames to inodes. The inode cache stores recently used inodes, while the dentry cache stores recent directory entries; each cached inode has a corresponding dentry.
Figure 5. inode structure and associated operations
Buffer Cache
Below the filesystem implementations lies the buffer cache, which tracks read/write requests from filesystems and block‑device drivers. It caches recently used buffers (pages) to reduce the number of physical I/O operations.
Interesting Filesystems
Linux supports many filesystems, including legacy ones like MINIX and MS‑DOS, journaling filesystems such as ext3, JFS, ReiserFS, encrypted filesystems (e.g., CFS), and virtual filesystems like /proc. The Filesystem in Userspace (FUSE) allows developers to implement filesystems in user space.
Conclusion
Although filesystem implementations are not overly complex, they exemplify a scalable, extensible architecture. Over decades, the Linux filesystem architecture has evolved to support a wide variety of filesystem types and storage devices, leveraging a plug‑in design and multi‑layered abstraction.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Linux
Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
