天天看點

Linux 檔案系統:procfs, sysfs, debugfs 用法簡介

轉自: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 來建立節點。