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:
/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 parametersFiles 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/devicespresents devices in their physical or firmware-discovered topology./sys/bus/<bus>/deviceslinks to devices participating in a bus./sys/bus/<bus>/driverspresents 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
opendoes 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:
- use one read operation or read loop for a small attribute;
- tolerate processes and devices disappearing with
ENOENT,ENODEV, or related errors; - validate identifiers again when acting on previously read state;
- 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
readreturns 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.
Comments
Read or comment on GitHub (sign-in required to comment)