调试文件系统(DebugFS )
Debugfs 是内核开发人员向用户空间提供信息的一种简单方法。与仅用于存储进程信息的 /proc 或具有严格“每个文件一个值”规则的 sysfs 不同,debugfs 根本没有任何规则。开发人员可以将任何他们想要的信息放在那里。debugfs 文件系统也不打算用作用户空间的稳定 ABI;理论上,对导出到那里的文件没有稳定性限制。然而,现实世界并不总是如此简单,即使是 debugfs 接口,在设计时也最好考虑到它们需要永远维护。
Debugfs 通常使用以下命令安装:
mount -t debugfs none /sys/kernel/debug
(或等效的 /etc/fstab 行)。默认情况下,只有 root 用户才能访问 debugfs 根目录。要更改对树的访问权限,可以使用“uid”、“gid”和“mode”挂载选项。
请注意,debugfs API 仅以 GPL 形式导出到模块。
使用 debugfs 的代码应该包含<linux/debugfs.h>。然后,第一件事是创建至少一个目录来保存一组 debugfs 文件:
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
如果成功,此调用将在指定的父目录下创建一个名为 name 的目录。如果 parent 为 NULL,则将在 debugfs 根目录中创建目录。成功时,返回值是一个 struct 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 表示应保存文件的目录,数据将存储在生成的 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为初始文件大小,其他参数与函数debugfs_create_file相同。
在许多情况下,创建一组文件操作实际上并不是必要的;debugfs 代码为简单情况提供了许多辅助函数。可以使用以下任何一种方式创建包含单个整数值的文件:
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);
这些文件支持读取和写入给定值;如果不应写入特定文件,只需相应地设置模式位。这些文件中的值是十进制的;如果十六进制更合适,可以使用以下函数:
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 类型的变量。
类似地,对于十进制和十六进制的无符号长整型变量也有辅助函数:
struct dentry *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,后跟换行符。如果写入,它将接受大写或小写值,或 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 结构指向的数据。一些驱动程序使用“blobs”作为返回几行(静态)格式化文本输出的简单方法。此函数可用于导出二进制信息,但主线中似乎没有任何代码可以这样做。请注意,使用创建的所有文件 debugfs_create_blob()都是只读的。
如果你想要转储一个寄存器块(在开发过程中这种情况经常发生,即使很少有这样的代码进入主线),debugfs 提供了两个功能:一个用于创建仅包含寄存器的文件,另一个用于在另一个顺序文件的中间插入一个寄存器块:
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 中转储 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 文件赋予新名称,可能位于不同的目录中。new_name 在调用之前不得存在;返回值是包含更新信息的 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 的指针,则该目录下的整个层次结构将被删除。