Loading learning content...
As Linux evolved through the early 2000s, the kernel faced a growing crisis. The /proc file system—originally designed for process information—had become a dumping ground for all manner of device data, driver parameters, and kernel internals. There was no consistent structure, no standardized interface, and navigating device relationships was nearly impossible.
The solution was sysfs (/sys)—a completely new virtual file system designed from the ground up to represent the kernel's unified device model. Where /proc grew organically, sysfs was architected deliberately. Every file, every directory, every symbolic link reflects the actual object relationships within the kernel.
Sysfs transformed Linux device management from chaos to clarity. Today, udev, systemd, container runtimes, and hardware configuration tools all depend on sysfs for device discovery, hotplug handling, and driver configuration.
By the end of this page, you will understand the architecture of sysfs and its relationship to the kernel's kobject infrastructure. You'll learn how devices, drivers, and buses are represented, how to navigate the sysfs hierarchy, and how user-space tools leverage sysfs for device management and configuration.
Before sysfs (Linux 2.5/2.6, circa 2003), device information was scattered across multiple locations:
/proc/bus/ — Bus-specific device listings/proc/scsi/ — SCSI device information/proc/ide/ — IDE device parameters/proc/driver/ — Driver-specific files/proc/sys/dev/ — Device tunable parametersioctl() calls — Device-specific control interfacesThe problems with this approach:
The unified device model solution:
The Linux 2.6 kernel introduced a unified device model with these core concepts:
Sysfs exposes this model as a file hierarchy where:
Every directory in sysfs corresponds to a kobject (kernel object) in kernel memory. Kobjects are the building blocks of the device model—they track reference counts, parent relationships, and attribute sets. When a kobject is created in the kernel, a sysfs directory appears automatically.
The sysfs mount point (/sys) contains several top-level directories, each organizing kernel objects by a different dimension:
| Directory | Contents | Organization Principle | Primary Users |
|---|---|---|---|
/sys/devices/ | All physical and virtual devices | By physical topology (bus hierarchy) | Hardware discovery, udev |
/sys/bus/ | Registered bus types | By bus type, with devices and drivers | Driver binding, bus enumeration |
/sys/class/ | Device classes | By functionality (net, block, tty) | Applications seeking specific device types |
/sys/block/ | Block devices | Shortcut to block device nodes | Storage management tools |
/sys/module/ | Loaded kernel modules | By module name | Module parameters, lsmod |
/sys/fs/ | File system parameters | By filesystem type | FS-specific tuning |
/sys/kernel/ | Kernel subsystems | Various kernel internals | Debug, tracing, security |
/sys/firmware/ | Firmware interfaces | ACPI, EFI, device tree | Firmware configuration |
/sys/power/ | Power management | Sleep states, PM controls | Power management daemons |
The same device appears in multiple places:
A network interface card (NIC) might appear in:
/sys/devices/pci0000:00/0000:00:1f.6/ — By physical location (PCI bus/device/function)/sys/bus/pci/devices/0000:00:1f.6/ — Listed under PCI bus devices/sys/class/net/eth0/ — By functional class (network interface)The /sys/class/ and /sys/bus/ entries are typically symbolic links to the canonical path in /sys/devices/. This design allows multiple views of the same underlying objects.
The /sys/devices/ directory hierarchy mirrors the physical device topology. Understanding this hierarchy reveals how hardware is connected in your system.
123456789101112131415161718192021222324252627282930313233343536373839404142
# Explore the device topology$ ls /sys/devices/LNXSYSTM:00 pci0000:00 platform pnp0 system virtual # Platform devices (motherboard-integrated, non-enumerable)$ ls /sys/devices/platform/8042 alarmtimer coretemp.0 efi-framebuffer.0i8042 pcspkr serial8250 ... # PCI bus hierarchy - the most common hardware bus$ ls /sys/devices/pci0000:00/0000:00:00.0 # Host bridge0000:00:01.0 # PCI Express Root Port0000:00:02.0 # Integrated graphics0000:00:14.0 # USB controller0000:00:1f.0 # ISA bridge0000:00:1f.2 # SATA controller0000:00:1f.6 # Ethernet controller # Follow a device path - NVMe SSD on PCI$ ls /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/class device driver enableirq local_cpulist modalias msi_busmsi_irqs nvme/ numa_node power/remove rescan reset resourcerevision subsystem subsystem_device ... # The nvme/ subdirectory contains the NVMe controller$ ls /sys/devices/pci0000:00/.../nvme/nvme0/firmware_rev model queue_count serialcntlid state transport ... # Block device exposed by NVMe$ ls /sys/devices/pci0000:00/.../nvme/nvme0/nvme0n1/alignment_offset capability dev device ... queue/ size ... # USB device topology - nested by hub/port$ ls /sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/ bcdDevice bConfigurationValuebDeviceClass bDeviceProtocol bDeviceSubClassconfiguration descriptors devnum...PCI addresses follow the format domain:bus:device.function (e.g., 0000:00:1f.6). This uniquely identifies the physical slot. USB paths show the port routing: usb1/1-2 means USB host controller 1, port 2. Deeply nested USB paths like 1-2.3.1 indicate hub hierarchies.
Virtual devices:
Not all devices have physical hardware. The /sys/devices/virtual/ directory contains kernel-created pseudo-devices:
$ ls /sys/devices/virtual/
block/ # Loopback, RAM disks, dm devices
net/ # Loopback (lo), bridges, VLANs
tty/ # Virtual terminals
dmi/ # Desktop Management Interface data
misc/ # Misc character devices
workqueue/ # Kernel workqueue statistics
These virtual devices use the same sysfs infrastructure as physical devices, enabling uniform management.
The /sys/bus/ directory organizes the kernel's bus subsystem registration. Each bus type directory contains device and driver subdirectories that expose the binding relationships.
123456789101112131415161718192021222324252627282930
# List registered bus types$ ls /sys/bus/acpi/ cpu/ i2c/ mmc/ pci/ serio/ usb/clocksource/ event_source/ memory/ node/ platform/ spi/ virtio/ # Each bus has devices/ and drivers/ subdirectories$ ls /sys/bus/pci/devices/ drivers/ drivers_autoprobe drivers_proberescan resource_alignment slots/ uevent # PCI devices - symlinks to actual device paths in /sys/devices/$ ls -la /sys/bus/pci/devices/lrwxrwxrwx ... 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0lrwxrwxrwx ... 0000:00:02.0 -> ../../../devices/pci0000:00/0000:00:02.0lrwxrwxrwx ... 0000:00:1f.6 -> ../../../devices/pci0000:00/0000:00:1f.6... # PCI drivers - each has bound devices$ ls /sys/bus/pci/drivers/ahci/ e1000e/ i915/ nvme/intel-lpss/ pcieport/ snd_hda_intel/ xhci_hcd/ # Which device is bound to the nvme driver?$ ls /sys/bus/pci/drivers/nvme/0000:01:00.0 -> ../../../../devices/pci0000:00/0000:00:01.0/0000:01:00.0bind module new_id remove_id uevent unbind # Manually unbind/rebind a device (advanced usage)$ echo "0000:01:00.0" | sudo tee /sys/bus/pci/drivers/nvme/unbind$ echo "0000:01:00.0" | sudo tee /sys/bus/pci/drivers/nvme/bindWhen a device is discovered, the kernel matches it against registered drivers using bus-specific matching (PCI vendor/device IDs, USB VID/PID, etc.). The driver's probe() function is called to initialize the device. Sysfs reflects this binding immediately—the device symlink appears under the driver directory.
While /sys/devices/ organizes by physical topology and /sys/bus/ by bus type, /sys/class/ organizes devices by functional category. This is the most useful view for applications that need specific device types.
| Class | Contents | Key Attributes | Example Use |
|---|---|---|---|
net/ | Network interfaces | address, mtu, operstate, speed | Network configuration (ip, ifconfig) |
block/ | Block devices | size, ro, queue/* | Disk management (fdisk, lsblk) |
tty/ | Terminal devices | dev, device | Serial port access |
input/ | Input devices | name, phys, id/* | Input device discovery |
hwmon/ | Hardware monitors | temp*, fan*, in* | Temperature/fan monitoring |
power_supply/ | Batteries, AC | status, capacity, voltage_now | Battery monitoring |
backlight/ | Display brightness | brightness, max_brightness | Screen brightness control |
leds/ | LED indicators | brightness, trigger | Keyboard/power LED control |
dmi/ | DMI/SMBIOS data | id/* | System identification |
drm/ | Graphics/display | card*/* | Display configuration |
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
# ═══════════════════════════════════════════════════════════════# Network interfaces# ═══════════════════════════════════════════════════════════════$ ls /sys/class/net/docker0 enp0s31f6 lo virbr0 wlp82s0 $ cat /sys/class/net/enp0s31f6/address00:1a:2b:3c:4d:5e $ cat /sys/class/net/enp0s31f6/speed1000 # 1000 Mbps = 1 Gbps $ cat /sys/class/net/enp0s31f6/operstateup # ═══════════════════════════════════════════════════════════════# Hardware monitoring (temperatures, fans)# ═══════════════════════════════════════════════════════════════$ ls /sys/class/hwmon/hwmon0 hwmon1 hwmon2 hwmon3 $ cat /sys/class/hwmon/hwmon0/namecoretemp # CPU temperature sensor $ cat /sys/class/hwmon/hwmon0/temp1_input45000 # 45.0°C (millidegrees) $ cat /sys/class/hwmon/hwmon0/temp1_labelCore 0 # ═══════════════════════════════════════════════════════════════# Backlight control# ═══════════════════════════════════════════════════════════════$ ls /sys/class/backlight/intel_backlight $ cat /sys/class/backlight/intel_backlight/max_brightness852 $ cat /sys/class/backlight/intel_backlight/brightness500 # Set brightness (requires appropriate permissions)$ echo 700 | sudo tee /sys/class/backlight/intel_backlight/brightness # ═══════════════════════════════════════════════════════════════# Battery status# ═══════════════════════════════════════════════════════════════$ cat /sys/class/power_supply/BAT0/statusCharging $ cat /sys/class/power_supply/BAT0/capacity67 # 67% $ cat /sys/class/power_supply/BAT0/voltage_now12456000 # 12.456V (microvolts)Entries in /sys/class/ are symlinks to the canonical device path in /sys/devices/. For example, /sys/class/net/eth0 → /sys/devices/pci0000:00/0000:00:1f.6/net/eth0. Following the link reveals the full physical topology.
Every file in sysfs represents an attribute of a kernel object. Understanding how attributes work reveals the elegant design behind sysfs.
Attribute characteristics:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
/* * Kernel code example: Defining a sysfs attribute * This creates a readable/writable attribute for a device */ #include <linux/device.h>#include <linux/sysfs.h> /* Show function - called when attribute file is read */static ssize_t my_value_show(struct device *dev, struct device_attribute *attr, char *buf){ struct my_device *mydev = dev_get_drvdata(dev); /* Format value as string, return byte count */ return sprintf(buf, "%d", mydev->value);} /* Store function - called when attribute file is written */static ssize_t my_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct my_device *mydev = dev_get_drvdata(dev); int value; /* Parse input */ if (kstrtoint(buf, 10, &value) < 0) return -EINVAL; /* Validate range */ if (value < 0 || value > 100) return -EINVAL; /* Apply the new value */ mydev->value = value; return count;} /* Macro creates struct device_attribute with proper naming */static DEVICE_ATTR_RW(my_value); /* Creates dev_attr_my_value */ /* Attribute group for bundling related attributes */static struct attribute *my_device_attrs[] = { &dev_attr_my_value.attr, &dev_attr_another_attr.attr, NULL, /* Null terminator required */}; ATTRIBUTE_GROUPS(my_device); /* Creates my_device_groups */ /* During device registration, attach attribute group */static int my_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; /* sysfs_create_groups does the magic */ return devm_device_add_groups(dev, my_device_groups);}The kobject hierarchy:
Kobjects form a tree structure in kernel memory. Each kobject has:
When you see a sysfs directory tree, you're seeing the kobject parent-child relationships.
Sysfs provides the data; udev (or systemd-udevd) acts on it. When a device is added or removed, the kernel generates a uevent notification. Udev receives this event, examines the device via sysfs, and applies rules to create device nodes, set permissions, and run scripts.
The hotplug flow:
123456789101112131415161718192021222324252627282930313233343536373839404142
# View uevent data for a device$ cat /sys/class/net/eth0/ueventDEVTYPE=INTERFACE=eth0IFINDEX=2 # More detailed uevent for a block device$ cat /sys/block/sda/ueventMAJOR=8MINOR=0DEVNAME=sdaDEVTYPE=disk # Monitor uevents in real-time$ udevadm monitormonitor will print the received events for:UDEV - the event which udev sends out after rule processingKERNEL - the kernel uevent # Plug in a USB drive...KERNEL[1234.567890] add /devices/pci0000:00/0000:00:14.0/usb1/1-2 (usb)KERNEL[1234.567891] add /devices/.../1-2/1-2:1.0 (usb)KERNEL[1234.567892] add /devices/.../1-2/1-2:1.0/host0 (scsi)KERNEL[1234.567893] add /devices/.../host0/target0:0:0/0:0:0:0/block/sdb (block)UDEV[1234.678901] add /devices/.../block/sdb (block) # Query device information as udev sees it$ udevadm info /sys/class/block/sdaP: /devices/pci0000:00/0000:00:17.0/ata3/host2/target2:0:0/2:0:0:0/block/sdaN: sdaL: 0S: disk/by-id/ata-Samsung_SSD_850_EVO_...S: disk/by-id/wwn-0x...S: disk/by-path/pci-0000:00:17.0-ata-1E: DEVPATH=/devices/pci0000:00/0000:00:17.0/ata3/host2/...E: DEVNAME=/dev/sdaE: DEVTYPE=diskE: MAJOR=8E: MINOR=0E: ID_VENDOR=ATAE: ID_MODEL=Samsung_SSD_850_EVOE: ID_SERIAL=Samsung_SSD_850_EVO_...You can force udev to re-read a device by echoing to its uevent file: echo change | sudo tee /sys/class/block/sda/uevent. This triggers rule re-evaluation without physical device changes—useful for debugging udev rules.
The /sys/module/ directory exposes information about loaded kernel modules and their parameters. This is where you can view and modify module behavior at runtime.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
# List loaded modules (same info as lsmod)$ ls /sys/module/acpi bridge cpufreq_powersave e1000e i915iwlmvm iwlwifi libata phy_enc28j60 snd_hda_intelusb_storage usbcore ... # Module directory contents$ ls /sys/module/e1000e/coresize holders/ initsize initstate notes/parameters/ refcnt sections/ srcversion taintuevent version # View module parameters$ ls /sys/module/e1000e/parameters/CrcStripping InterruptThrottleRate SmartPowerDownEnableIntMode KumeranLockLoss debug # Read a parameter value$ cat /sys/module/e1000e/parameters/InterruptThrottleRate3 # Dynamic interrupt throttling enabled # Module version$ cat /sys/module/e1000e/version3.8.7-k # Reference count (how many things depend on this module)$ cat /sys/module/e1000e/refcnt1 # ═══════════════════════════════════════════════════════════════# Modifying module parameters at runtime# ═══════════════════════════════════════════════════════════════ # Enable debug output for iwlwifi (Wi-Fi driver)$ cat /sys/module/iwlwifi/parameters/debug0 $ echo 0xFFFFFFFF | sudo tee /sys/module/iwlwifi/parameters/debug# (shows all debug messages in dmesg) # Note: Not all parameters are writable at runtime# Check permissions: -r--r--r-- = read-only, -rw-r--r-- = writable by root # ═══════════════════════════════════════════════════════════════# Built-in "modules" (compiled into kernel, not loadable)# ═══════════════════════════════════════════════════════════════ # Even built-in subsystems appear here$ ls /sys/module/kernel/parameters/ $ ls /sys/module/kernel/parameters/consoleblank panic panic_on_oops panic_on_rcu_stall pause_on_oopsperf_cpu_time_max_percent...Like /proc/sys changes, module parameter modifications via sysfs are ephemeral. To make them persistent, add options to /etc/modprobe.d/ configuration files: options e1000e InterruptThrottleRate=1. The module will load with these parameters at boot.
Let's explore real-world scenarios where sysfs knowledge is essential:
12345678910111213141516171819202122232425
# Find all block devices and their sizesfor dev in /sys/block/*/; do name=$(basename "$dev") # Skip loop and ram devices [[ "$name" == loop* || "$name" == ram* ]] && continue # Get size in bytes (512-byte sectors) size_sectors=$(cat "$dev/size" 2>/dev/null || echo 0) size_gb=$((size_sectors * 512 / 1024 / 1024 / 1024)) # Get device model (for physical drives) model=$(cat "$dev/device/model" 2>/dev/null | tr -s ' ' || echo "N/A") # Check if rotational (HDD) or SSD rotational=$(cat "$dev/queue/rotational" 2>/dev/null) [[ "$rotational" == "1" ]] && type="HDD" || type="SSD" echo "$name: ${size_gb}GB $type - $model"done # Output:# sda: 500GB SSD - Samsung SSD 860 EVO# sdb: 2000GB HDD - WDC WD20EZRX-00D# nvme0n1: 1000GB SSD - Samsung SSD 970 EVO PlusSysfs represents the maturation of Linux's device model, providing a structured, consistent interface to kernel objects that replaced the chaos of scattered /proc entries.
/sys/devices/ (topology), /sys/bus/ (by bus type), /sys/class/ (by function)/sys/module/ exposes and allows runtime modification of module settingsWhat's next:
We've explored virtual file systems that expose kernel information. Next, we'll examine tmpfs—a memory-backed file system that combines the performance of RAM with the convenience of file semantics. tmpfs powers /tmp, /run, and many other transient storage needs in modern Linux systems.
You now understand sysfs architecture, the kobject model, device class organization, udev integration, and practical applications. This knowledge is essential for Linux device driver development, system administration, and hardware troubleshooting.