← Previous Lecture | Next Lecture →
Every professional Linux device driver exposes at least a few tunable settings or status values to user space, and the kernel community has one clear answer for how to do it: sysfs in Linux kernel development is the sanctioned, upstream-approved interface for device attributes — not procfs, and not a custom character device ioctl. This lesson explains what sysfs actually is, how it connects to the modern device model, and how to create attribute files using the APIs that are current on kernel 6.x.
What You Will Learn
- What sysfs is and how it is backed by kernfs internally
- The different “viewports” sysfs offers into the device model
- How to create a sysfs attribute file with
device_create_file() - Why modern drivers prefer attribute groups over one-off file creation
- The “one value per file” rule and why it exists
Prerequisites
- Completion of the previous procfs lecture in this free Linux kernel development course
- Basic understanding of struct device and the Linux Device Model (bus, device, driver)
- A working kernel module build setup
What Is sysfs?
sysfs is a virtual, in-memory filesystem, conventionally mounted at /sys, that exposes the kernel’s internal representation of every bus, device, and driver currently active on the system. Since kernel 3.14, sysfs has been implemented on top of a lightweight, generic virtual filesystem layer called kernfs, which was split out specifically so that sysfs and cgroupfs could share the same low-level tree-management code. As a driver author you never touch kernfs directly — you work through the sysfs and device-model APIs, and the kernel generates and tears down the actual files for you automatically as devices appear and disappear.
The Viewports Into the Device Model
Rather than one flat list, sysfs organizes the same underlying devices into several different views, each useful for a different purpose.
| Viewport | Path | Purpose |
|---|---|---|
| Devices | /sys/devices | The canonical, authoritative tree of every device |
| Bus | /sys/bus | Devices grouped by the bus they are attached to (platform, USB, I2C, SPI…) |
| Class | /sys/class | Devices grouped by function (network, input, backlight…) regardless of bus |
| Block | /sys/block | Legacy view of block storage devices |
| /sys/devices/…/mydev | ↔ | Single real device object in the kernel | ↔ | /sys/class/mydrv/mydev (symlink) |
Why sysfs, Not procfs, for Drivers
procfs has no structural convention: any driver could put arbitrarily formatted multi-value text into a single file, which made scripting against it fragile. sysfs enforces a simple, strict convention instead: one value per file. This single rule is what allows generic user-space tools such as udev and system monitoring utilities to reliably read and write individual attributes without parsing free-form text.
Creating a sysfs Attribute File
The classic, still-supported way to create a single sysfs file for a device is device_create_file(), which takes a struct device * and a struct device_attribute * describing the file’s name, permissions, and show/store callbacks.
#include <linux/device.h>
static ssize_t temperature_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int temp_millideg = 42000; /* read from real hardware here */
return sysfs_emit(buf, "%d\n", temp_millideg);
}
static DEVICE_ATTR_RO(temperature);
static int mydrv_probe(struct platform_device *pdev)
{
int ret;
ret = device_create_file(&pdev->dev, &dev_attr_temperature);
if (ret)
return ret;
return 0;
}
Two modernization notes worth calling out compared to older references:
- sysfs_emit() replaced the older pattern of calling
sprintf()directly into the sysfs buffer. It was introduced to guarantee the write never exceeds the fixedPAGE_SIZEbuffer sysfs hands you, closing a whole class of overflow bugs. DEVICE_ATTR_RO()/DEVICE_ATTR_RW()macros are now preferred over manually filling adevice_attributestruct, since they automatically generate the correctly nameddev_attr_*variable and wire up permissions consistently.
Grouping Attributes Instead of Creating Them One by One
Real-world drivers rarely stop at one attribute. Calling device_create_file() repeatedly for every attribute, and remembering to call device_remove_file() for each one on teardown, is error-prone. The modern recommended pattern is to bundle related attributes into a struct attribute_group and pass that group to the device at registration time, so the kernel core creates and destroys all of them together as a single atomic step.
Architecture: Device Model Building Blocks
| Bus (platform, I2C, USB…) |
→ | Device (struct device) |
→ | Driver bound to it (struct device_driver) |
Every specialized device type — platform_device, i2c_client, usb_device, and similar structures — embeds a generic struct device inside itself. That embedded structure is what you pass into sysfs attribute APIs, which is why sysfs integrates so cleanly regardless of what kind of bus your hardware sits on.
Real-World Use Case
A typical embedded sensor driver exposes read-only attributes such as current temperature or firmware version, and read-write attributes such as sampling rate or power mode, all as separate sysfs files grouped under the device’s own directory. This lets a system administrator or a shell script simply cat or echo a value without writing any custom tooling.
Common Mistakes and Troubleshooting
| Mistake | Fix |
|---|---|
Using sprintf() instead of sysfs_emit() in a show callback |
Switch to sysfs_emit() to stay within the PAGE_SIZE buffer safely |
| Packing multiple values into one attribute file | Split into one file per value to follow sysfs convention |
| Forgetting to remove attribute files on device removal | Use attribute groups so removal happens automatically with the device |
Best Practices
- Prefer attribute groups over individual
device_create_file()calls for anything beyond one attribute - Always use
sysfs_emit()in show callbacks on current kernels - Keep each file to a single, simply-parsable value
- Set permissions as restrictively as the use case allows
Performance Considerations
sysfs reads and writes go through user-context callbacks, so avoid blocking operations or heavy computation inside show/store functions; cache values where possible and refresh them on a timer or interrupt instead of doing expensive work on every read.
Security Considerations
Never expose raw kernel addresses through a sysfs file. Validate all input length and content in store callbacks before acting on it, since any user with file permission can write arbitrary strings, and always double-check the permission bits you assign match the sensitivity of the underlying operation.
Summary / Key Takeaways
- sysfs is the kernel-recommended interface for device attributes, built on kernfs since kernel 3.14
- It exposes the same devices through multiple viewports: devices, bus, class, and block
- Use
sysfs_emit()andDEVICE_ATTR_*macros on current kernels instead of older sprintf-based patterns - Group related attributes with
struct attribute_groupfor cleaner creation and teardown
Conclusion
With this lesson, you now understand sysfs in Linux kernel programming well enough to expose clean, convention-following attribute files from your own drivers. Combined with the previous procfs lecture, you have the full picture of why the kernel community moved driver interfacing away from procfs and standardized on sysfs and the device model. Continue with the next lecture in this free Linux kernel development course to see a complete platform device example built from scratch.
Frequently Asked Questions
Q1. What is the difference between sysfs and procfs?
procfs is a general text-based interface historically used for process and misc kernel info, while sysfs is a structured, one-value-per-file interface specifically tied to the device model and recommended for driver attributes.
Q2. Is sysfs backed by a real filesystem on disk?
No, sysfs is entirely in-memory and virtual, generated dynamically as devices appear and removed as they disappear.
Q3. What is kernfs?
kernfs is a shared, generic virtual filesystem layer introduced in kernel 3.14 that both sysfs and cgroupfs build on top of.
Q4. Why should I use sysfs_emit() instead of sprintf()?
sysfs_emit() guarantees the output never exceeds the fixed PAGE_SIZE buffer that sysfs provides, preventing a class of buffer overflow bugs common in older driver code.
Q5. Can one sysfs file hold multiple values?
Technically yes, but it violates sysfs convention and breaks compatibility with generic tooling; the accepted rule is one value per file.
Q6. What are attribute groups used for?
They let you register and unregister a whole set of related sysfs attributes as a single atomic operation instead of managing each file individually.
Q7. Do I need a real hardware device to experiment with sysfs?
No, you can create a simple platform device in your own module purely to obtain a valid struct device to attach sysfs files to, which is exactly what the next lecture demonstrates.
This lesson is part of EmbeddedPathashala’s free Linux kernel development course, part of our broader free embedded systems course covering drivers from first principles.
Browse the Free Course Next Lecture: Platform Devices →
2 Comments