Understanding Linux VFS: How the Virtual File System Manages Files, Inodes, and Superblocks
This article explains the Linux virtual file system (VFS) architecture, its role in abstracting concrete file systems, and details core kernel structures such as superblocks, inodes, dentries, file objects, and their relationships to processes, providing code examples and diagrams for clarity.
1. What is a file system?
A file system is the software component of an operating system that manages storage and retrieval of file information, organizing data on storage devices and providing mechanisms for creating, reading, writing, and deleting files.
2. Linux file system layers
Linux separates file system handling into two layers: the Virtual File System (VFS) and concrete file systems (e.g., ext2, ext3, ext4, sysfs, proc). The VFS provides a uniform interface for user‑space applications, while each concrete file system implements the actual on‑disk layout.
3. Position of VFS in the Linux architecture
From a user perspective, the file system stack consists of three layers: the system‑call interface, the VFS layer, and the mounted concrete file systems. The VFS translates POSIX calls into operations on the appropriate concrete file system.
4. How VFS provides transparent file handling
VFS abstracts multiple underlying file systems (ext2, ext3, fat32, ntfs, etc.) so that applications can perform operations like read or write without knowing the specific file system type. The VFS maintains an in‑memory directory tree (the dentry cache) and resolves file names to inodes, which contain metadata about the actual data blocks.
5. Superblock (super_block) structure
The superblock represents a mounted file system instance and stores global information such as device ID, block size, maximum file size, and pointers to operation tables.
struct super_block {
struct list_head s_list; /* linked list of superblocks */
dev_t s_dev; /* device identifier */
unsigned char s_blocksize_bits;
unsigned long s_blocksize; /* block size in bytes */
loff_t s_maxbytes; /* maximum file size */
struct file_system_type *s_type; /* file system type (ext2, etc.) */
const struct super_operations *s_op;/* superblock operations */
struct dquot_operations *dq_op; /* quota operations */
struct quotactl_ops *s_qcop; /* quota control */
unsigned long s_flags; /* mount flags */
unsigned long s_magic; /* magic number */
struct dentry *s_root; /* root directory dentry */
struct rw_semaphore s_umount; /* umount synchronization */
int s_count; /* usage count */
atomic_t s_active; /* reference count */
/* ... many additional fields omitted for brevity ... */
};Key fields
s_list : pointer to the global superblock list.
s_dev : block‑device identifier (e.g., 0x301 for /dev/hda1).
s_blocksize_bits and s_blocksize : size of a block expressed in bits and bytes.
s_maxbytes : maximum file size supported by the file system.
s_type : the file system type (ext2, fat32, …).
s_op : pointer to the super_operations table.
s_root : dentry of the mounted root directory.
6. Superblock operations (struct super_operations)
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*dirty_inode)(struct inode *, int flags);
int (*write_inode)(struct inode *, struct writeback_control *wbc);
int (*drop_inode)(struct inode *);
void (*evict_inode)(struct inode *);
void (*put_super)(struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs)(struct super_block *);
int (*unfreeze_fs)(struct super_block *);
int (*statfs)(struct dentry *, struct kstatfs *);
int (*remount_fs)(struct super_block *, int *, char *);
void (*umount_begin)(struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
/* quota related methods omitted */
};7. Inode (struct inode) structure
An inode stores metadata about a file (size, permissions, timestamps, block pointers, etc.). There are VFS inodes (in memory) and concrete file‑system inodes (on disk).
struct inode {
umode_t i_mode; /* permission bits */
unsigned short i_opflags;
kuid_t i_uid; /* owner UID */
kgid_t i_gid; /* owner GID */
unsigned int i_flags; /* FS flags */
const struct inode_operations *i_op; /* inode ops */
struct super_block *i_sb; /* associated superblock */
struct address_space *i_mapping;/* address space */
dev_t i_rdev; /* device ID for special files */
loff_t i_size; /* file size */
struct timespec i_atime; /* last access */
struct timespec i_mtime; /* last modification */
struct timespec i_ctime; /* last status change */
unsigned long i_nlink; /* hard link count */
/* ... many additional fields omitted ... */
};Important inode methods (struct inode_operations)
create() : called by VFS to create a new inode when a regular file is created in a directory.
lookup() : locate a dentry for a given name.
link() : create a hard link.
unlink() : remove a hard link.
mkdir() : create a subdirectory.
rmdir() : remove a subdirectory.
mknod() : create special files (device nodes, pipes, sockets).
8. Dentry (struct dentry) structure
Dentries represent directory entries in memory; they form a cache that speeds up path lookup. Each dentry points to an inode (or is negative if the name does not exist).
struct dentry {
unsigned int d_flags; /* protected by d_lock */
seqcount_t d_seq; /* per‑dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent;/* parent directory */
struct qstr d_name; /* name */
struct inode *d_inode; /* associated inode (NULL if negative) */
unsigned char d_iname[DNAME_INLINE_LEN];
struct lockref d_lockref; /* lock and refcount */
const struct dentry_operations *d_op; /* dentry ops */
struct super_block *d_sb; /* superblock of the tree */
unsigned long d_time; /* revalidation time */
void *d_fsdata;/* FS‑specific data */
struct list_head d_lru; /* LRU list */
union {
struct list_head d_child; /* children list */
struct rcu_head d_rcu; /* RCU callback */
} d_u;
struct list_head d_subdirs;/* sub‑directory list */
struct hlist_node d_alias; /* inode alias list */
};9. File object (struct file)
A file represents an open file descriptor. Multiple processes can hold separate file structures that reference the same inode.
struct file {
union {
struct llist_node fu_llist; /* linked list of opened files */
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path; /* contains dentry and vfsmount */
const struct file_operations *f_op; /* file ops */
spinlock_t f_lock; /* protects f_flags, f_ep_links */
atomic_long_t f_count; /* reference count */
unsigned int f_flags; /* open flags */
fmode_t f_mode; /* access mode */
struct mutex f_pos_lock;
loff_t f_pos; /* file offset */
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra; /* readahead state */
u64 f_version;
void *private_data; /* driver‑specific data */
/* EPOLL, security, and other optional fields omitted */
};Key fields
f_flags and f_mode : control how the process accesses the file.
f_pos : current file offset (independent per descriptor).
f_count : reference count; the file is closed only when this reaches zero.
f_op : pointer to the file_operations table that implements read, write, mmap, etc.
10. Files_struct and fs_struct
files_structholds the per‑process table of open file descriptors, while fs_struct stores the process’s namespace information such as root directory, current working directory, and umask.
struct files_struct {
atomic_t count;
rwlock_t file_lock; /* protects the fields below */
int max_fds; /* maximum number of file descriptors */
int max_fdset;
int next_fd; /* next free descriptor */
struct file **fd; /* pointer to the descriptor array */
fd_set *close_on_exec; /* descriptors to close on exec */
fd_set *open_fds; /* bitmap of open descriptors */
fd_set close_on_exec_init;
fd_set open_fds_init;
struct file *fd_array[NR_OPEN_DEFAULT];
};
struct fs_struct {
atomic_t count;
rwlock_t lock;
int umask;
struct dentry *root, *pwd, *altroot; /* root, cwd, and alternate root */
struct vfsmount *rootmnt, *pwdmnt, *altrootmnt;
};11. Relationship between a process and the VFS structures
The kernel’s task_struct contains a pointer to a files_struct. Each entry in files_struct.fd is a file (the file descriptor). A file points to a dentry ( f_dentry), which points to an inode ( d_inode). The inode’s i_fop (file operations) is obtained from the superblock’s s_op. This chain links a user‑space file descriptor to the actual on‑disk file.
12. Disk partitions and file systems
A physical disk can be divided into multiple partitions, each mounted with a different file system type (ext2, ext3, fat32, etc.). The VFS treats each mounted file system uniformly, while the underlying superblock and inode structures handle the specifics of the on‑disk layout.
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.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
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.
