Virtual File Systems

Last update: 2021-04-17 01:39:12 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Linux uses filesystem interfaces for both stored data and live kernel state. The Virtual Filesystem Switch (VFS) makes those objects accessible through common system calls, while pseudo-filesystems such as procfs and sysfs generate their contents from kernel objects. Similar path syntax does not give a pseudo-file the persistence or I/O semantics of a disk file.

VFS as a Kernel Abstraction #

The VFS is the kernel layer behind operations such as open, read, write, stat, and mount. It represents mounted filesystems through common object types:

  • a superblock represents a filesystem instance, while a mount attaches that instance at a location in one mount namespace;
  • an inode represents an object and its metadata;
  • a dentry associates a name with an inode and participates in pathname lookup;
  • a file represents an open instance and its current position;
  • an address space connects cacheable file contents to the page cache.

Filesystem implementations provide operation tables for relevant objects. ext4 can map reads to persistent blocks, NFS can issue network requests, and procfs can format a kernel structure. Callers still receive file descriptors and use the same system- call boundary.

“Virtual filesystem” is therefore ambiguous. VFS names the abstraction layer; a pseudo-filesystem is one filesystem implementation whose objects do not correspond to durable files on a storage volume. tmpfs is also memory-backed, but it provides ordinary mutable file semantics and distinct data per mount. procfs and sysfs expose purpose-built kernel interfaces instead.

Procfs Process and System Views #

Procfs is conventionally mounted at /proc. Numeric directories expose processes, while nonnumeric entries expose system-wide state and control interfaces:

text
/proc/1234/status      formatted state for process 1234
/proc/1234/fd/         links for its open file descriptors
/proc/self/            the reading process's own proc directory
/proc/meminfo          memory-accounting snapshot
/proc/sys/             sysctl parameters

Files under /proc/<pid> are checked against process credentials, ptrace access rules, capabilities, mount options, and security modules. A pathname being readable by root in one context does not make it public to an unprivileged process.

Instances, PID Namespaces, and Mount Namespaces #

Procfs output is namespace-sensitive. A procfs mount associated with a PID namespace shows tasks visible in that namespace; /proc/self and /proc/thread-self also resolve relative to the caller. A process creating or entering another PID namespace must use an appropriate procfs mount to obtain a matching process view.

Mount namespaces independently control where and whether that procfs instance is mounted. Containers can consequently have different /proc mount points, read-only settings, and visibility options while sharing the same kernel. Options such as hidepid restrict access to other users’ process directories, and subset=pid can hide top-level entries unrelated to tasks on supporting kernels.

Procfs should not be modeled as one global directory tree cached by pathname. The same path can resolve to different information for different callers, namespaces, credentials, or moments in time. Open descriptors to a dead process do not become handles to a later process that reuses the same PID.

Sysfs and the Driver Object Model #

Sysfs, conventionally mounted at /sys, exports the kernel’s object model. A registered kobject receives a directory beneath its parent, so directory hierarchy expresses object relationships and controls lifetime. The Linux device model builds devices, drivers, buses, and classes on that infrastructure:

  • /sys/devices presents devices in their physical or firmware-discovered topology.
  • /sys/bus/<bus>/devices links to devices participating in a bus.
  • /sys/bus/<bus>/drivers presents registered drivers and their bindings.
  • /sys/class/<class> groups devices by function, such as network or block devices.

Many entries outside /sys/devices are symbolic-link views of the same underlying device object. Scripts should resolve documented attributes rather than infer physical topology from a convenient class path.

A sysfs attribute connects a small file to subsystem-defined show and store callbacks. The core allocates a page-sized buffer and asks show to render a value or passes one write buffer to store. Attributes should contain one ASCII value, or an array of values of the same type, because simple text forms are inspectable and avoid embedding an ad hoc structured protocol. Binary attributes exist for data that cannot follow that convention and have separate callbacks.

Treat Text Files as ABI Endpoints #

Text does not mean informal. A documented procfs or sysfs file is a userspace ABI with file-specific syntax:

  • Read the complete value and parse only forms promised by its ABI documentation.
  • Include the expected newline when writing conventional text attributes.
  • Do not depend on undocumented whitespace, column positions, directory ordering, or the presence of testing-only interfaces.
  • Check write errors and short writes; a successful open does not imply that a proposed value is accepted.

Mode bits and file permissions are only one access-control layer. Capability checks, ownership, namespaces, lockdown policy, and Linux Security Modules can reject an operation. World-writable sysfs attributes are intentionally discouraged because a write often changes live device or driver state.

The kernel’s Documentation/ABI tree classifies interfaces as stable, testing, obsolete, or removed. Software tied to a testing attribute accepts a different compatibility commitment from software using a stable one. Feature detection should check for a documented path and handle ENOENT; kernel version comparisons cannot account for vendor backports and configuration choices.

Reads Are Snapshots, Not Transactions #

Many pseudo-files are generated when read. Kernel state can change between opening, reading, and parsing a file, or between reads of two related files. Even one large procfs report may summarize fields observed at slightly different times. Rewinding a sysfs attribute to offset zero can invoke its show callback again and return a new value.

Consequently, multiple reads do not provide an atomic system snapshot unless the specific ABI promises one. Robust consumers should:

  1. use one read operation or read loop for a small attribute;
  2. tolerate processes and devices disappearing with ENOENT, ENODEV, or related errors;
  3. validate identifiers again when acting on previously read state;
  4. use a dedicated transactional API when consistency across objects is required.

Some sysfs attributes support poll/select notification when their subsystem calls the sysfs notification mechanism. Such notification is opt-in per attribute, not a property of every file under /sys. Consumers generally reopen or seek to offset zero and reread after an event. Procfs entries likewise have file-specific polling semantics.

Pseudo-Files Are Not Persistent Storage #

Several ordinary-file assumptions fail for procfs and sysfs:

  • reported size can be zero even though read returns data;
  • mmap, arbitrary seeks, append mode, or partial writes may be unsupported;
  • directory entries can appear and disappear as processes and devices change;
  • a successful write can trigger hardware activity rather than store retrievable bytes;
  • data vanishes on shutdown and is reconstructed from the next kernel’s state.

Configuration that must survive reboot belongs in a persistent configuration system whose startup logic deliberately writes the required kernel interfaces. Copying /proc or /sys cannot back up kernel state, and restoring copied files cannot recreate processes or devices.

References #

Comments

Read or comment on GitHub (sign-in required to comment)