Linux Virtual Filesystem

  • Operating System
  • Linux
Contents

Virtual Filesystem Intro

VFS Design: VFS is designed to be flexible and modular, allowing new file systems to be added easily. As new storage and network technologies are developed, VFS ensures that the operating system can interact with them. Through a common API, VFS provides consistent system calls for file operations such as open, read, write, and close, no matter the file system type.

VFS enables the “everything is a file” abstraction in Unix-like systems.

Filesystem Interface

The VFS is the glue that enables system calls such as open(), read(), and write() to work regardless of the filesystem or underlying physical medium. Such a generic interface is feasible only because the kernel implements an (Filesystem) abstraction layer around its low-level filesystem interface.

Example: ret = write (fd, buf, len);

On one side of the system call is the generic VFS interface, providing the frontend to user-space; on the other side of the system call is the filesystem-specific backend, dealing with the implementation details.

Unix Filesystems Concepts

In Unix, filesystems are mounted at a specific mount point in a global hierarchy known as a namespace. This enables all mounted filesystems to appear as entries in a single tree. Different filesystems (which could even be on different devices or remote locations) are attached to specific directories in the Unix file hierarchy.

VFS Objects and Data Structures

The four primary object types of theVFS are:

  • The superblock object, which represents a specific mounted filesystem.
  • The inode object, which represents a specific file.
  • The dentry object, which represents a directory entry, which is a single component of a path.
  • The file object, which represents an open file associated with a process.

operations object is contained within each of these primary objects.These objects describe the methods that the kernel invokes against the primary objects.

The operations objects are implemented as a structure of pointers to functions that operate on the parent object.

  • The super_operations object contains methods that the kernel can invoke on a specific filesystem, such as write_inode() and sync_fs().
  • The inode_operations object contains methods that the kernel can invoke on a specific file, such as create() and link().
  • The dentry_operations object contains methods that the kernel can invoke on a specific directory entry, such as d_compare() and d_delete().
  • The file_operations object contains methods that a process can invoke on an open file, such as read() and write().

VFS loves structures. It has more than the primary objects mentioned before. Each registered filesystem is represented by file_system_type structure, describing the filesystem and its capabilities. Each mount point is represented by vfsmount structure, containing information such as location and mount flags.

The Superblock Object

struct super_block defined in <linux/fs.h>

Superblock Operation

Due to the lack of object-oriented support in C, there is no way for the method to easily obtain its parent, so you have to pass it. An example of a same code between C and C++:

sb->s_op->write_super(sb); // C
sb.write_super();          // C++

The Inode Object

struct inode and is defined in <linux/fs.h>

Inode Operation

inode_operations defined in <linux/fs.h>

The Dentry Object

Context: VFS treats directories as a type of file. In the path /bin/vi, both bin and vi are files - bin being the special directory file and vi being a regular file. Despite this useful unification, VFS often needs to perform directory-specific operations, such as path name lookup. Path name lookup involves translating each component of a path, ensuring it is valid, and following it to the next component.

struct dentry defined in <linux/dcache.h>

  • Unlike the previous two objects, the dentry object does not correspond to any sort of on-disk data structure. Dentry object is not physically stored on the disk.
  • TheVFS creates it on-the-fly from a string representation of a path name.

Dentry State

  1. used: A used dentry corresponds to a valid inode (d_inode points to an associated inode) and indicates that there are one or more users of the object (d_count is positive). A used dentry is in use by the VFS and points to valid data, and thus cannot be discarded.

  2. unused: An unused dentry refers to a valid inode(d_inode pointing to an inode), but the VFS is not currently using the dentry object (d_count is zero). Dentry object is kept around as a cache in case it is needed again.

    By caching the dentry instead of destroying it early, the dentry won’t need re-creation if it’s needed in the future. This speeds up path name lookups. If memory needs to be reclaimed, the dentry can be discarded because it’s not in use.

  3. negative: not associated with a valid inode (d_inode is NULL) because either the inode was deleted or the path name was never correct to begin with. The dentry is kept around, however, so that future lookups are resolved quickly.

    Consider a daemon that tries to read a missing config file, causing costly failed lookups. Caching these negative results can be worthwhile.

Dentry Cache

The dentry cache consists of three parts:

  1. Lists of “used” dentries linked off their associated inode via the i_dentry field of the inode object.
  2. A doubly linked “least recently used” list of unused and negative dentry objects.

    The list is inserted at the head, such that entries toward the head of the list are newer than entries toward the tail. When the kernel must remove entries to reclaim memory, the entries are removed from tail; those are the oldest and presumably have the least chance of being used in the near future.

  3. A hash table and hashing function used to quickly resolve a given path into the associated dentry object.

    Hash table is represented by the dentry_hashtable array. Each element is a pointer to a list of dentries that hash to the same value.


Example: Assume that you are editing a source file in your home directory, /home/dracula/src/the_sun_sucks.c. Each time this file is accessed (for example, when you first open it), the VFS must follow each directory entry to resolve the full path:/,home,dracula,src,and finally the_sun_sucks.c.To avoid this time-consuming operation each time this path name is accessed, the VFS can first try to look up the path name in the dentry cache. If the lookup succeeds, the required final dentry object is obtained without serious effort. Conversely, the VFS must manually resolve the path by walking the filesystem for each component of the path. After this task is completed,the kernel adds the dentry objects to the dcache to speed up any future lookups.

Dentry Operation

dentry_operations structure is defined in <linux/dcache.h>

The File Object

The file object is the in-memory representation of an open file.The object (but not the physical file) is created in response to the open() system call and destroyed in response to the close() system call. Because multiple processes can open and manipulate a file at the same time, there can be multiple file objects in existence for the same file.

struct file and is defined in <linux/fs.h>

File Operations

Some Important Operations:

Data Structures Associated with Filesystems

file_system_type

file_system_type is defined in <linux/fs.h>

  • get_sb() function reads the superblock from the disk and populates the superblock object when the filesystem is loaded.

vfsmount

vfsmount structure is defined in <linux/mount.h>

  • It’s complicated to maintain the relation between the filesystem and all the other mount points. The various linked lists in vfsmount keep track of this information.

  • mnt_flags specifies the standard mount flags

    These flags are useful on removable devices that the admin does not trust.

Data Structures Associated with a Process

  1. files_struct is used by the Linux kernel to keep track of all of the open files for a process.

    • The array fd_array points to the list of open file objects. NR_OPEN_DEFAULT is 64, but you can adjust this macro as needed.
  2. fs_struct contains filesystem information related to a process and is pointed at by the fs field in the process descriptor.

  3. namespace enable each process to have a unique view of the mounted filesystems on the system—not just a unique root directory, but an entirely unique filesystem hierarchy.

    • The list member specifies a doubly linked list of the mounted filesystems that make up the namespace.

Conclusion

Linux supports a wide range of file systems, from native file systems such as ext3 and ext4, to networked file systems such as NFS and Coda. There are more than 60 file systems alone in the official kernel.

The VFS layer provides these disparate file systems with both a framework for their implementation and an interface for working with the standard system calls. The VFS layer makes it easy to implement new file systems in Linux and enables those file systems to automatically interoperate via the standard Unix system calls.

← All posts