網址:http://www.tinylab.org/show-the-usage-of-procfs-sysfs-debugfs/
1 前言
內核中有三個常用的偽文件系統:procfs,debugfs和sysfs。
- procfs — The proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
- sysfs — The filesystem for exporting kernel objects.
- debugfs — Debugfs exists as a simple way for kernel developers to make information available to user space.
它們都用於Linux內核和用戶空間的數據交換,但是適用的場景有所差異:
- procfs 歷史最早,最初就是用來跟內核交互的唯一方式,用來獲取處理器、內存、設備驅動、進程等各種信息。
- sysfs 跟 kobject 框架緊密聯系,而 kobject 是為設備驅動模型而存在的,所以 sysfs 是為設備驅動服務的。
- debugfs 從名字來看就是為debug而生,所以更加靈活。
它們仨的掛載方式類似,做個實驗:
$ sudo mkdir /tmp/{proc,sys,debug}$ sudo mount -t proc nondev /tmp/proc/$ sudo mount -t sys nondev /tmp/sys/$ sudo mount -t debugfs nondev /tmp/debug/
不過,默認情況下,它們分別掛載在/proc,/sys/,/sys/kernel/debug/。
下面簡單介紹這三個文件系統的用法。在介紹之前,請記下他們的官方文檔:
- procfs — Documentation/filesystems/proc.txt
- sysfs — Documentation/filesystems/sysfs.txt
- debugfs — Documentation/filesystems/debugfs.txt
2 debugfs
-
API說明
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)struct dentry *debugfs_create_file(const char *name, umode_t mode,struct dentry *parent, void *data,const struct file_operations *fops)
-
參考實例
drivers/base/power/wakeup.c:
/*** wakeup_sources_stats_show - Print wakeup sources statistics information.* @m: seq_file to print the statistics into.*/static int wakeup_sources_stats_show(struct seq_file *m, void *unused){struct wakeup_source *ws;seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t""expire_count\tactive_since\ttotal_time\tmax_time\t""last_change\tprevent_suspend_time\n");rcu_read_lock();list_for_each_entry_rcu(ws, &wakeup_sources, entry)print_wakeup_source_stats(m, ws);rcu_read_unlock();return 0;}static int wakeup_sources_stats_open(struct inode *inode, struct file *file){return single_open(file, wakeup_sources_stats_show, NULL);}static const struct file_operations wakeup_sources_stats_fops = {.owner = THIS_MODULE,.open = wakeup_sources_stats_open,.read = seq_read,.llseek = seq_lseek,.release = single_release,};static int __init wakeup_sources_debugfs_init(void){wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources",S_IRUGO, NULL, NULL, &wakeup_sources_stats_fops);return 0;}
-
創建完的接口
/sys/kernel/debug/wakup_sources
-
給接口添加多級目錄
上述接口直接創建在
debugfs根目錄(/sys/kernel/debug)下,所以debugfs_create_file的parent參數被設置成了NULL,如果要加一級目錄,則可以先用debugfs_create_dir創建一級目錄,例如,要創建:/sys/kernel/debug/power/wakeup_sources的話,則需要:struct dentry *power;int err = -ENOMEM;power = debugfs_create_dir("power", NULL);if (!power)return err;wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources",S_IRUGO, power, NULL, &wakeup_sources_stats_fops);
3 procfs
-
API說明
static inline struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)static inline struct proc_dir_entry *proc_create(const char *name, umode_t mode,struct proc_dir_entry *parent, const struct file_operations *proc_fops)
-
參考實例
在上面例子的基礎上,可以添加如下語句:
static int __init wakeup_sources_debugfs_init(void){proc_create("wakelocks", S_IFREG | S_IRUGO, NULL, &wakeup_sources_stats_fops);return 0;}
-
創建后的接口
/proc/wakelocks
-
給接口添加多級目錄
這樣創建的接口用起來跟
/sys/kernel/debug/wakeup_sources沒有任何差異,類似地,如果要加一級目錄,例如/proc/power/wakelocks,則可以:struct proc_dir_entry *power;int err = -ENOMEM;power = proc_mkdir("power", NULL);if (!power)return err;proc_create("wakelocks", S_IFREG | S_IRUGO, power, &wakeup_sources_stats_fops);
proc_mkdir用法跟debugfs_create_dir幾無差異。
4 sysfs
-
API說明
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)static inline int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name)int device_create_file(struct device *dev, const struct device_attribute *attr)
-
參考實例
在
/sys/power下創建一個 wakelocks 節點,用於讀/寫一個字符串。static char test_str[11];static ssize_t show_wakelocks(struct kobject *kobj, struct attribute *attr, char *buf){int ret;ret = snprintf(buf, 10, "%s\n", test_str);return ret;}static ssize_t store_wakelocks(struct kobject *kobj, struct attribute *attr,const char *buf, size_t count){int tmp;ret = sscanf(buf, "%10s", test_str);if (ret != 1)return -EINVAL;return count;}define_one_global_rw(wakelocks);static int __init wakelocks_init(void){int ret;ret = sysfs_create_file(power_kobj, &wakelocks.attr);}
-
創建后的節點
/sys/power/test_node
-
給接口添加多級目錄
咱們上面其實已經把 test_node 創建在
/sys/power目錄下,而非根目錄/sys下,而參數 power_kobj 為內核已經在kernel/power/main.c創建的kobject對象。struct kobject *power_kobj;power_kobj = kobject_create_and_add("power", NULL);if (!power_kobj)return -ENOMEM;
在
sysfs中,有另外一個常見用法,那就是在一個 kobject 對應的目錄下創建一個符號(屬性文件)指向另外一個 kobject 對應的目錄,通常這個是為了方便記憶和訪問。這個API是sysfs_create_link。這種創建符號鏈接方法其實有一個很特殊的實例,那就是在驅動模型里頭,有一個
class的概念,它把掛在不同總線上,但是實現類似功能的設備進行歸類,比如說input類,backlight類等。如果設備屬於一個現存的類,比如
backlight,那么可以用backlight_device_register創建,如果是I2C設備,會先在I2C下創建sysfs訪問節點,並創建一個符號鏈接到backlight類所屬的目錄下。當然,如果沒有找到設備能掛的直觀的類,也可以用
class_create創建類,設備類通常會有一組默認的設備操作接口,例如backlight類有bl_device_attributes,如果要創建更多的設備特定的節點,可以用device_create_file或者device_add_groups創建節點或者節點群。
5 小結
通過比較發現,上述三個文件系統的 API 用法類似,而其中 debugfs 和 procfs 幾乎有相同的參數,用的主要結構體是 struct file_operations,蠻多操作可以用 seq_* 家族的函數來實現。而 sysfs 則用到比較簡單一些的 struct global_attr 結構體。對於提供給用戶空間的節點,都可以輕松實現讀寫操作。
在創建目錄方面,debugfs 和 procfs 類似,且比較簡單。而 sysfs 要創建一級目錄,需要先創建一個 kobject 對象。
為了簡化設備模型依據總線創建的訪問節點路徑,sysfs 提供了API用於創建更簡易的符號鏈接,可以創建到自己指定的目錄下,也可以用設備類(Class)提供的API創建到設備類所屬的目錄下。
對於 sysfs,由於 kobject 與 device 的一對一依存關系,也可以直接用 device_create_file 來創建節點。
