linux内核中的debugfs原来可以这样玩!
一、相关文件
- /fs/debugfs/file.c:debugfs的file描述文件。
- /fs/debugfs/inode.c:debugfs的inode描述文件。
- /fs/debugfs/internal.h:用于debugfs的内部声明。
二、简介
debugfs
可用于内核向用户空间提供信息,debugfs
是个小型的文件系统,与/proc
和sysfs
不同,debugfs没有较为严苛的规则和定义,我们可以在里面放置想要的任何信息,以便于系统开发和调试。
通常使用如下命令安装debugfs
:
mount -t debugfs none /sys/kernel/debug
或者:
mount -t debugfs debugfs /sys/kernel/debug/
也可以在/etc/fstab
文件中使用等效的语句:
默认情况下,在一些发行版的linux系统中,只有root用户可以访问debugfs根目录。
注意,在内核源码中,debugfs API仅以GPL方式导出到模块。
三、debugfs的API
1、在debugfs中创建目录
使用debugfs的代码应包含<linux/debugfs.h>
头文件。然后是创建至少一个目录来保存一组debugfs文件,可使用下列API实现:
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
当函数执行成功后,将在指定的父目录下创建一个名为name
的目录,如果parent
为NULL,则该目录将在debugfs根目录中创建。函数执行成功后,返回一个指向struct dentry
的指针,可用于在目录中创建文件。如果返回值为ERR_PTR(-ERROR)则表明出现了问题,如果返回ERR_PTR(-ENODEV)
,则表明内核是在没有debugfs
支持的情况下构建,这时候相关API将失效。
2、在debugfs目录中创建文件
在debugfs目录中创建文件的常用API是:
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
是一个实现文件行为的一组文件操作。struct file_operations
中包含了关于文件操作的很多接口函数,此处至少应提供read()
和write()
操作,其他操作可以根据实际情况实现。该函数返回值将是指向所创建文件的dentry
指针,如果发生错误,则返回ERR_PTR(-ERROR)
,如果缺少debugfs
支持,则返回ERR_PTR(-ENODEV)
。
3、创建一个具有初始大小的文件
创建一个具有初始大小的文件,可以使用以下API:
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
相同。
4、创建包含单个整数值(十进制)的文件
在多数情况下,创建一组文件操作并不是必需的,这时候可以使用以下助手函数创建包含单个整数值的文件:
//创建包含u8整数值的文件
void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, u8 *value);
//创建包含u16整数值的文件
void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, u16 *value);
////创建包含u32整数值的文件
void debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, u32 *value);
//创建包含u64整数值的文件
void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent, u64 *value);
这些文件支持读取和写入给定值;如果不支持写入特定文件,只需相应设置模式位即可,使用上述API创建的文件中的值是十进制的。
5、创建包含单个十六进制值的文件:
如果需要设置十六进制,可以使用以下API函数:
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_create_size_t()
函数将创建一个debugfs
文件来表示size_t
类型的变量。
6、创建包含unsigned long 类型的变量的文件
对于十进制和十六进制的 unsigned long 类型的变量可使用以下助手函数:
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);
7、创建包含布尔类型的文件
对于布尔值可使用下列API函数:
void debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value);
8、创建包含atomic_t类型的值的文件
atomic_t
值可使用以下API函数放置在debugfs中:
void debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent, atomic_t *value)
读取该文件将获取atomic_t
值,写入该文件将设置atomic_t
值。
9、创建包含二进制数据块的文件
也可以导出二进制数据块,数据块具有以下结构和功能:
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提供了两个函数:1、创建一个只有寄存器的文件。2、在另一个顺序文件的中间位置插入一个寄存器块:
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);
debugfs_print_regs32()
中的base
参数可能为0,但可能希望使用__stringify
构建reg32数组,许多寄存器名(宏)实际上是寄存器基数上的字节偏移量。
10、创建u32数组的文件
如果想在debugfs中转储一个u32数组,可使用以下API:
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
参数包装了一个指向数组数据及其元素数量的指针。
注意:一旦数组被创建,它的大小不能被改变。
11、创建与设备相关的seq_file
有一个助手函数可用于创建与设备相关的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内容。
12、为debugfs中的文件重命名
如果想要重命名debugfs目录下的文件名,可使用以下API:
struct dentry *debugfs_rename(struct dentry *old_dir,struct dentry *old_dentry,
struct dentry *new_dir,const char *new_name);
调用debugfs_rename()
将为现有的debugfs
文件(可能在不同的目录中)提供一个新名称,在调用debugfs_rename()
之前必须不存在new_name
,返回值是带有更新后信息的old_dentry
。
13、为debugfs目录中的文件创建符号链接
符号链接可通过debugfs_create_symlink()
创建:
struct dentry *debugfs_create_symlink(const char *name,struct dentry *parent,const char *target);
14、删除debugfs创建的目录或者文件
在debugfs中创建的所有目录都不会自动清除。如果在没有显式删除debugfs项的情况下卸载了一个模块,这时结果将是出现大量过时的指针,还可能会出现一些奇怪的行为。因此,必须存在删除创建的所有文件和目录的操作和入口点。
可使用以下API删除文件:
void debugfs_remove(struct dentry *dentry);
如果dentry
值是NULL或错误值,这时候将不会删除任何内容。
使用下列API可以删除整个目录层级结构,在调试的时候可以使用:
void debugfs_remove_recursive(struct dentry *dentry);
如果将指向与顶级目录对应的dentry
的指针传递给该debugfs_remove_recursive()
,这时候该目录下的整个层次结构将被删除。
更多API可参见文末附上的参考链接。
四、实验代码
在本小节中,将使用上述提到的API在debugfs中创建目录,并导出相应的参数描述文件,然后在命令行中对其进行查看,首先设计代码:
/**
* @file debugfs_demo.c
* @author your name (you@domain.com)
* @brief debugfs api usage
* @version 0.1
* @date 2023-08-17
*
* @copyright Copyright (c) 2023
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define BUFFER_SIZE 256
static char buffer[BUFFER_SIZE];
static struct dentry *debugfs_demo_dir;
static u8 u8data = 90;
static u32 boolData = false;
static struct dentry *general_file, *u8data_dentry , *x8data_dentry, *bool_dentry;
static int general_file_open (struct inode *inode, struct file *file)
{
printk(KERN_INFO"do general_file_open ops\n");
return 0;
}
static ssize_t general_file_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
return simple_read_from_buffer(ubuf,size,loff,buffer,BUFFER_SIZE);
}
static ssize_t general_file_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
if(size > BUFFER_SIZE)return -EINVAL;
return simple_write_to_buffer(buffer,BUFFER_SIZE,loff,ubuf,size);
}
static struct file_operations general_file_ops =
{
.open = general_file_open,
.read = general_file_read,
.write = general_file_write
};
static char data[4]={0x01,0x05,0x12,0x23};
static struct debugfs_blob_wrapper blobData = {data,4};
static int __init debugfs_demo_init(void)
{
//1、create debugfs_demo_dir dir in debugfs
debugfs_demo_dir = debugfs_create_dir("debugfs_demo_dir",NULL);
if (!debugfs_demo_dir) {
pr_err(" failed to create debugfs entry debugfs_demo_dir\n");
return -1;
}
//2、create general_file in debugfs_demo_dir
general_file = debugfs_create_file("general_file",0644,debugfs_demo_dir,NULL,&general_file_ops);
//3、create u8data in debugfs
u8data_dentry = debugfs_create_u8("u8data",0644,debugfs_demo_dir,&u8data);
//4、create x8data in debugfs
x8data_dentry = debugfs_create_x8("x8data",0644,debugfs_demo_dir,&u8data);
//5、create boolData in debugfs
bool_dentry = debugfs_create_bool("boolData", 0644, debugfs_demo_dir, &boolData);
//6、create blobData in debugfs
debugfs_create_blob("blobData",0644,debugfs_demo_dir,&blobData);
printk(KERN_INFO"debugfs demo create successful\n");
return 0;
}
static void __exit debugfs_demo_exit(void)
{
debugfs_remove_recursive(debugfs_demo_dir);
printk(KERN_INFO"debugfs_demo_exit\n");
}
module_init(debugfs_demo_init);
module_exit(debugfs_demo_exit);
MODULE_AUTHOR("iriczhao");
MODULE_LICENSE("GPL");
在上述代码中,将在debugfs中创建一个名为debugfs_demo_dir
的目录,并且在该目录中导出五种类型的数据:
- 1、通用文件数据:general_file,值默认没指定
- 2、以十进制导出数据:u8data,值为90
- 3、以十六进制导出数据:x8data,值为0x5a
- 4、布尔类型数据:boolData,值为N
- 5、blob类型数据:blobData,值为0x01,0x05,0x12,0x23
将上述代码以模块方式构建后(模块名debugfs_demo.ko)拷贝到目标平台中,使用mount
命令查看目前已挂载的文件系统:
发现并没有挂载debugfs,这时候使用以下命令可以手动挂载debugfs:
mount -t debugfs debugfs /sys/kernel/debug/
接着将debugfs_demo.ko加载进内核,完成后将路径切换进/sys/kernel/debug:
这时候看到期望的debugfs目录debugfs_demo_dir
导出成功,然后切换进该目录中:
看见了在驱动程序中创建的五个文件,分别查看一下数据:
从输出结果分析,数据符合驱动程序运行后预期的结果!
五、参考链接
1、https://github.com/torvalds/linux/blob/master/Documentation/filesystems/debugfs.rst
2、debugfs的API接口:https://docs.kernel.org/filesystems/api-summary.html?highlight=debugfs#the-debugfs-filesystem
文章转载自公众号嵌入式小生
- 分享
- 举报
-
浏览量:3071次2019-08-21 17:25:46
-
浏览量:1996次2019-08-08 17:11:28
-
浏览量:538次2023-08-22 17:27:47
-
浏览量:1144次2022-01-12 09:00:14
-
浏览量:2339次2019-11-06 15:06:58
-
浏览量:2105次2018-05-18 14:10:39
-
浏览量:4048次2021-01-13 17:36:11
-
浏览量:3339次2020-01-20 10:10:37
-
浏览量:567次2023-08-18 17:31:53
-
浏览量:810次2023-08-18 14:08:55
-
浏览量:1184次2023-08-18 10:36:32
-
浏览量:3441次2020-04-27 16:58:40
-
浏览量:2769次2017-11-20 16:03:39
-
浏览量:2951次2022-05-26 10:04:36
-
浏览量:2814次2020-11-06 09:40:21
-
浏览量:2227次2020-08-28 15:08:37
-
浏览量:2616次2020-08-12 20:09:12
-
浏览量:1076次2023-08-14 16:26:07
-
浏览量:1097次2023-08-11 18:18:49
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
小菜很菜
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明