Linux - 如何使用debugfs

本文介绍Linux内核中的debugfs文件系统,它专为调试目的设计。文章解释如何配置和挂载debugfs,以及如何在驱动程序中利用debugfs API创建目录和文件。还提供了创建不同类型文件的具体函数和示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Linux内核中,许多驱动程序和子系统都支持一个叫做debugfs的特殊文件系统。Debugfs与其他虚拟文件系统非常相似,比如sysfs和procfs,但它除了提供调试信息外没有任何功能用途,方便内核开发人员将内核信息在user space中可见。这个文件系统中的文件是由内核生成的,其内容会因驱动/子系统的不同而不同。

/proc文件系统只表示一个进程的有关信息;sysfs有严格的一个文件一个值的规则;而debugfs并没有什么规则约束,开发者可以把信息放在想要放的地方。而且debugfs也并不是作为一个稳定的user space的ABI所存在的,并不存在一个文件系统规范。

当编写新的驱动程序时,debugfs比printk语句更受欢迎,因为它更容易启用/禁用,并且提供了一个更标准化的接口。本文档提供了关于使用和实现debugfs文件的基本信息。

先决条件 / Prerequisites

使用以下配置选项构建内核:

CONFIG_DEBUG_FS=y

Mounting debugfs

Debugfs可以像其他虚拟文件系统一样使用mount命令来挂载。

1,创建一个挂载点。默认的是/sys/kernel/debug/,它可能已经存在。

# mkdir /sys/kernel/debug

2,使用mount命令挂载文件系统。

# mount -t debugfs none /sys/kernel/debug

3,Use the cat command to view the contents of the file system:

# cat /sys/kernel/debug/gpio

我的板子,ingenic x2000的输出结果:

gpiochip0: GPIOs 0-31, parent: platform/10010000.pinctrl, GPA:

gpio-1   (                    |pwdn                ) out hi ACTIVE LOW

gpiochip1: GPIOs 32-63, parent: platform/10010000.pinctrl, GPB:

gpio-32  (                    |vcc-en              ) out lo

gpio-36  (                    |i2c-pow             ) out lo

gpio-51  (                    |Trigger             ) in  hi IRQ ACTIVE LOW

gpiochip2: GPIOs 64-95, parent: platform/10010000.pinctrl, GPC:

gpio-64  (                    |pwm0                ) in  lo

gpio-74  (                    |pwm10               ) in  lo

gpio-75  (                    |pwm11               ) in  lo

gpiochip3: GPIOs 96-127, parent: platform/10010000.pinctrl, GPD:

gpio-100 (                    |BT-Reset            ) in  lo IRQ ACTIVE LOW

gpiochip4: GPIOs 128-159, parent: platform/10010000.pinctrl, GPE:

#

Adding debugfs Support to a Driver 

debugfs API在内核源文件fs/debugfs/inode.c和fs/debugfs/file.c中有注释描述。

为一个给定的驱动或子系统创建debugfs文件是相对容易的。它有三个主要步骤:

- 确定目录结构

- 为每个文件创建文件操作函数

- 在debugfs文件系统中注册文件。

* Determine directory structure

* Create file operation functions for each file.

* Register files with debugfs filesystem.

Debugfs Directory Structure

你可以把你的debugfs条目分成多个文件,甚至可以把它们组织成一个分层的目录结构。单个文件可以用于记录寄存器的值,或用于读取缓冲区的内容,或者是你想要的任何内容。最终,试着选择一种对正在调试问题的人最有帮助的结构。

层次结构是由debugfs_create_dir和debugfs_create_file函数决定的,这些函数在文档和内核源代码中会有更详细的描述。

File Operation Functions 

在大多数情况下,你需要定义文件操作的函数:

- open

- release

- llseek

- read

你要为每个文件定义操作。如果通用的文件操作适合你的文件,你也许可以使用。

一旦函数在驱动程序中被定义,它们必须被分配到file_operations结构中相应的函数指针中。每个文件都应该有一个该结构。

参见Linux Device Drivers 3rd的第三章:Linux Device Drivers, 3rd Edition | Timesys LinuxLink

Registering Files

函数 debugfs_create_file 用来在 debugfs 文件系统中注册一个文件。这个函数需要一个文件名、文件权限掩码和file_operations结构(以及其他东西)作为参数。这个函数应该在驱动/子系统与内核初始化时为每个文件调用一次。

要在debugfs文件系统中创建子目录,使用函数debugfs_create_dir。

这些函数在内核源代码中的fs/debugfs目录中都有记录。

Code Samples

在Linux内核中,有非常多的代码实例,使用了debugfs。比如说:

* drivers/gpio/gpiolib.c - GPIOLIB subsystem

* drivers/usb/gadget/atmel_ubsa_udc.c - Atmel USBA Gadget Ethernet Driver

具体操作

内核代码中使用debugfs功能,需要包含头文件:<linux/debugfs.h>。

第一件事,就是要创建一个文件夹,用来存放文件:

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

这个调用,如果成功,将在指定的父目录下创建一个名为name的目录。如果父目录是NULL,目录将在debugfs根目录下创建。如果成功,返回值是一个结构dentry指针,可以用来在目录中创建文件(并在最后清理)。ERR_PTR(-ERROR)的返回值表示出了问题。如果返回ERR_PTR(-ENODEV),说明内核构建时不支持debugfs,相关函数不会工作。

在debugfs的文件夹中创建文件的一般方法是:

struct dentry *debugfs_create_file(const char *name, umode_t mode,

                                   struct dentry *parent, void *data,

                                   const struct file_operations *fops);

这里,name是要创建的文件的名称,mode描述了该文件应该具有的访问权限,parent表示应该保存该文件的目录,data将被保存在产生的inode结构的i_private字段中,fops是一组实现该文件行为的文件操作函数。至少应该提供read()和/或write()操作;其他操作可以根据需要加入。同样,返回值将是一个指向所创建文件的dentry指针,出错时为ERR_PTR(-ERROR),如果缺少debugfs支持,则为ERR_PTR(-ENODEV)。

创建一个指定初始大小的文件,则使用下面函数:

void debugfs_create_file_size(const char *name, umode_t mode,

                              struct dentry *parent, void *data,

                              const struct file_operations *fops,

                              loff_t file_size);

file_size表示文件的初始化大小,其他的参数和上面一样。

在有些情况下,有些文件的创建不用这么麻烦,提供一些helper函数,来方便处理简单的情况,比如一个文件内容只包含一个整数的话:

void debugfs_create_u8(const char *name, umode_t mode,

                       struct dentry *parent, u8 *value);

void debugfs_create_u16(const char *name, umode_t mode,

                        struct dentry *parent, u16 *value);

void debugfs_create_u32(const char *name, umode_t mode,

                        struct dentry *parent, u32 *value);

void debugfs_create_u64(const char *name, umode_t mode,

                        struct dentry *parent, u64 *value);

这些文件支持读取和写入给定的值;如果某个文件不可写,只需修改mode参数的相关bits。这些文件中的数值是十进制的;如果十六进制更合适,可以用以下函数代替:

void debugfs_create_x8(const char *name, umode_t mode,

                       struct dentry *parent, u8 *value);

void debugfs_create_x16(const char *name, umode_t mode,

                        struct dentry *parent, u16 *value);

void debugfs_create_x32(const char *name, umode_t mode,

                        struct dentry *parent, u32 *value);

void debugfs_create_x64(const char *name, umode_t mode,

                        struct dentry *parent, u64 *value);

只要开发者知道要输出的值的范围大小,这些函数就很有用。不过,有些类型在不同的架构上会有不同的宽度,使情况变得有些复杂。有一些函数是为了帮助解决这种特殊情况的:

void debugfs_create_size_t(const char *name, umode_t mode,

                           struct dentry *parent, size_t *value);

正如预期的那样,这个函数将创建一个debugfs文件来代表一个size_t类型的变量。

类似的,还有变量类型为unsigned long的helper函数,包括十进制和十六进制的:

void debugfs_create_ulong(const char *name, umode_t mode,

                                    struct dentry *parent,

                                    unsigned long *value);

void debugfs_create_xul(const char *name, umode_t mode,

                        struct dentry *parent, unsigned long *value);

布尔值在debugfs中的存放方法:

void debugfs_create_bool(const char *name, umode_t mode,

                         struct dentry *parent, bool *value);

对这个文件的读取将产生Y(对于非零值)或N,后面是一个新行符。如果写入文件,它将接受大写或小写的字母(Y或N),或者1或0,任何其他的输入将被默认忽略。

还有,atomic_t的值存放在debugfs中:

void debugfs_create_atomic_t(const char *name, umode_t mode,

                             struct dentry *parent, atomic_t *value)

读这个文件得到atomic_t的值,写入此文件atomic_t的值。

另一个选择是导出一个任意二进制数据块,结构和函数接口如下:

struct debugfs_blob_wrapper {

    void *data;

    unsigned long size;

};

struct dentry *debugfs_create_blob(const char *name, umode_t mode,

                                   struct dentry *parent,

                                   struct debugfs_blob_wrapper *blob);

对该文件的读取将返回debugfs_blob_wrapper结构所指向的数据的指针。一些驱动程序使用 "blob "作为返回几行(静态)格式化文本输出的简单方法。这个函数可以用来输出二进制信息,但在linux mainline上似乎没有代码这样用过。注意,所有用debugfs_create_blob()创建的文件都是只读的。

如果你想转储(dump)一个寄存器块(这在开发过程中经常发生,即使很少有这样的代码提交到mainline。Debugfs提供了两个函数:一个是制作一个只包含寄存器的文件,另一个是在另一个序列(sequential)文件的中间插入一个寄存器块:

struct debugfs_reg32 {

    char *name;

    unsigned long offset;

};

struct debugfs_regset32 {

    const struct debugfs_reg32 *regs;

    int nregs;

    void __iomem *base;

    struct device *dev;     /* Optional device for Runtime PM */

};

debugfs_create_regset32(const char *name, umode_t mode,

                        struct dentry *parent,

                        struct debugfs_regset32 *regset);

void debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,

                     int nregs, void __iomem *base, char *prefix);

“base"参数的值可能是0,但你可能想用__stringify建立reg32数组,一些寄存器名称(宏)实际上是相对寄存器块的基准地址的字节偏移量。

如果你想在debugfs中转储(dump)一个u32数组,你可以用以下方法创建文件:

struct debugfs_u32_array {

    u32 *array;

    u32 n_elements;

};

void debugfs_create_u32_array(const char *name, umode_t mode,

                    struct dentry *parent,

                    struct debugfs_u32_array *array);

参数 "array "包装了一个指向数组数据的指针和其元素的数量。注意:一旦数组被创建,其大小就不能被改变。

有一个辅助函数来创建与设备相关的seq_file:

void debugfs_create_devm_seqfile(struct device *dev,

                             const char *name,

                             struct dentry *parent,

                             int (*read_fn)(struct seq_file *s,

                                     void *data));

参数 "dev "是与这个debugfs文件相关的设备,"read_fn "是一个函数指针,它被调用来打印seq_file的内容。

还有几个面向目录的辅助函数:

struct dentry *debugfs_rename(struct dentry *old_dir,

                              struct dentry *old_dentry,

                              struct dentry *new_dir,

                              const char *new_name);

struct dentry *debugfs_create_symlink(const char *name,

                                      struct dentry *parent,

                                      const char *target);

调用debugfs_rename()将给现有的debugfs文件起一个新的名字,可能在不同的目录下。新名称在调用前必须不存在;返回值是带有更新信息的old_dentry。符号链接可以用debugfs_create_symlink()创建。

有一件重要的事情,所有debugfs用户都必须考虑到:在debugfs中创建的任何目录都没有自动清理。 如果一个模块在卸载前没有明确删除debugfs条目,结果将是大量无效指针和各种不可知行为。所以所有的debugfs用户--至少是那些可以作为模块构建的用户--必须准备好删除他们创建的所有文件和目录。一个文件可以用以下方法删除:

void debugfs_remove(struct dentry *dentry);

dentry值可以是NULL或一个错误值,在这种情况下,什么都不会被删除。

很久以前,debugfs用户需要记住他们创建的每个debugfs文件的dentry指针,以便所有的文件都能被清理掉。不过现在我们生活在更先进的时代,现在debugfs用户可以调用:

void debugfs_remove_recursive(struct dentry *dentry);

如果这个函数被传递给一个与顶级目录相对应的dentry的指针,该目录下面的整个层次结构将被删除。

参考:

https://docs.kernel.org/filesystems/debugfs.html

How To Use debugfs | Timesys LinuxLink

<think>好的,我现在需要帮助用户解决如何挂载debugfs文件系统的问题。用户已经提供了一些引用内容,我需要先仔细阅读这些引用,确保正确理解相关信息和步骤。 首先,查看用户提供的引用内容。引用[1]提到了debugfs_mount和mnt_sb,这可能涉及到debugfs的挂载状态。引用[2]是关于重新挂载文件系统的命令,但用户的问题是关于初次挂载。引用[4]详细说明了debugfs_create_dir函数的使用,这可能是在内核模块中创建debugfs目录的方法,但用户的问题是关于使用mount命令挂载,所以这可能属于内核开发中的使用,而用户可能是在用户空间操作。引用[5]提到debugfs中的control文件节点,说明动态输出相关的信息,但同样属于使用debugfs后的功能。 根据我的知识,debugfs通常是作为内核调试用的文件系统,挂载在/sys/kernel/debug目录下。通常挂载debugfs的命令是: mount -t debugfs none /sys/kernel/debug 这里可能需要确认用户是否有权限执行该操作,以及系统是否支持debugfs。需要检查内核配置是否启用了CONFIG_DEBUG_FS选项,否则debugfs可能不可用。 用户提供的引用[4]中的说明提到,如果父目录为NULL,则创建在/sys/kernel/debug下,这进一步确认了默认的挂载点。所以正确的步骤应该是使用mount命令将debugfs挂载到该目录。 另外,用户可能需要使用sudo来获得权限,否则可能挂载失败。需要提醒用户这一点。 总结步骤: 1. 确保内核支持debugfs,通常默认在发行版中是启用的。 2. 创建挂载点目录,通常/sys/kernel/debug可能已经存在。 3. 执行挂载命令:sudo mount -t debugfs none /sys/kernel/debug 4. 验证挂载是否成功,可以使用mount命令查看或检查目录是否存在。 可能的问题: - 用户可能没有权限,需要使用sudo。 - 目录不存在,需要先创建。 - 内核未启用debugfs,需要重新编译内核或加载模块。 根据用户提供的引用,引用[1]中的代码可能涉及内核模块中的debugfs操作,但用户的问题是关于挂载,所以主要关注mount命令的使用。同时,引用[3]中的目录结构可能是一个示例项目的路径,但可能与挂载步骤无关。 现在需要将这些步骤清晰地呈现给用户,并提醒注意事项,如权限和内核配置。同时,生成相关问题,帮助用户进一步了解debugfs使用和相关操作。</think>### 挂载debugfs文件系统的步骤 1. **确认内核支持debugfs** debugfsLinux内核提供的调试文件系统,默认在大多数发行版中已启用。若需确认,可通过以下命令检查内核配置(需安装`linux-headers`或对应源码): ```bash grep CONFIG_DEBUG_FS /boot/config-$(uname -r) ``` 若输出结果为`CONFIG_DEBUG_FS=y`,则表示已启用;若为`m`,则需加载`debugfs`模块。 2. **创建挂载点目录(若不存在)** debugfs通常挂载到`/sys/kernel/debug`目录,该目录可能已存在。若不存在,需手动创建: ```bash sudo mkdir -p /sys/kernel/debug ``` 3. **执行挂载命令** 使用`mount`命令挂载debugfs,需`root`权限: ```bash sudo mount -t debugfs none /sys/kernel/debug ``` 参数说明: - `-t debugfs`:指定文件系统类型为debugfs- `none`:由于debugfs是虚拟文件系统,无需指定设备源。 - `/sys/kernel/debug`:目标挂载点路径。 4. **验证挂载状态** 执行以下命令查看是否挂载成功: ```bash mount | grep debugfs ``` 输出应类似: ```bash none on /sys/kernel/debug type debugfs (rw,relatime) ``` 5. **设置开机自动挂载(可选)** 编辑`/etc/fstab`文件,添加以下行: ```bash none /sys/kernel/debug debugfs defaults 0 0 ``` ### 注意事项 - **权限问题**:挂载操作需`root`权限,普通用户需使用`sudo`[^1]。 - **内核未启用debugfs**:若内核未编译`CONFIG_DEBUG_FS`,需重新配置内核并启用该选项[^4]。 - **动态输出功能**:挂载后可通过`/sys/kernel/debug/tracing`目录使用动态调试功能[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值