Fundamentals 34 min read

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.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Understanding Linux VFS: How the Virtual File System Manages Files, Inodes, and Superblocks

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.

Linux VFS and concrete file system layers
Linux VFS and concrete file system layers

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.

Linux VFS architecture diagram
Linux VFS architecture diagram

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_struct

holds 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.

Process to superblock, inode, dentry, file relationship diagram
Process to superblock, inode, dentry, file relationship diagram

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

KernelLinuxprocessVFSSuperblockDentry
Liangxu Linux
Written by

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.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.