`
coolerbaosi
  • 浏览: 721940 次
文章分类
社区版块
存档分类
最新评论

Linux内核里的DebugFS

 
阅读更多

DebugFS,顾名思义,是一种用于内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据。类似的虚拟文件系统还有procfs和sysfs等,这几种虚拟文件系统都并不实际存储在硬盘上,而是Linux内核运行起来后才建立起来。

通常情况下,最常用的内核调试手段是printk。但printk并不是所有情况都好用,比如打印的数据可能过多,我们真正关心的数据在大量的输出里不是那么一目了然;或者我们在调试时可能需要修改某些内核变量,这种情况下printk就无能为力,而如果为了修改某个值重新编译内核或者驱动又过于低效,此时就需要一个临时的文件系统可以把我们需要关心的数据映射到用户空间。在过去,procfs可以实现这个目的,到了2.6时代,新引入的sysfs也同样可以实现,但不论是procfs或是sysfs,用它们来实现某些debug的需求,似乎偏离了它们创建的本意。比如procfs,其目的是反映进程的状态信息;而sysfs主要用于Linux设备模型。不论是procfs或是sysfs的接口应该保持相对稳定,因为用户态程序很可能会依赖它们。当然,如果我们只是临时借用procfs或者sysfs来作debug之用,在代码发布之前将相关调试代码删除也无不可。但如果相关的调试借口要在相当长的一段时间内存在于内核之中,就不太适合放在procfs和sysfs里了。故此,debugfs应运而生。

默认情况下,debugfs会被挂载在目录/sys/kernel/debug之下,如果您的发行版里没有自动挂载,可以用如下命令手动完成:

# mount -t debugfs none /your/debugfs/dir

Linux内核为debugfs提供了非常简洁的API,本文接下来将以一个实作为例来介绍,sample code可以从这里下载。

这个实作会在debugfs中建立如下的目录结构:

其中,a对应模块中的一个u8类型的变量,b和subdir下面的c都是对应模块里的一个字符数组,只是它们的实现方式不同。

在module_init里,我们首先要建立根目录mydebug:

1
my_debugfs_root = debugfs_create_dir("mydebug", NULL);

第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里。

子目录也是用debugfs_create_dir来实现:

1
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);

建立文件a的代码非常简单:

1
debugfs_create_u8("a", 0644, my_debugfs_root, &a);

这表示文件名为“a”,文件属性是0644,父目录是上面建立的“mydebug”,对应的变量是模块中的a。

Linux内核还提供了其他一些创建debugfs文件的API,请参考本文的附录。

b是一个32-bytes的字符数组,在debugfs里,数组可以用blob wrapper来实现。

1
2
3
4
5
6
char hello[32] = "Hello world!\n";
struct debugfs_blob_wrapper b;
b.data = (void *)hello;
b.size = strlen(hello) + 1;
debugfs_create_blob("b", 0644, my_debugfs_root, &b);

这里需要注意的是,blob wrapper定义的数据只能是只读的。在本例中,虽然我们把文件b的权限设定为0644,但实际这个文件还是只读的,如果试图改写这个文件,系统将提示出错。

如果需要对内核数组进行写的动作,blob wrapper就无法满足要求,我们只能通过自己定义文件操作来实现。在这个实作里,可以参考文件c的实现。c和b在模块里对应着同一块字符数组,不同的是,b是只读的,而c通过自定义的文件操作同时实现了读和写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static int c_open(struct inode *inode, struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
}
static ssize_t c_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
if (*ppos >= 32)
return 0;
if (*ppos + count > 32)
count = 32 - *ppos;
if (copy_to_user(buffer, hello + *ppos, count))
return -EFAULT;
*ppos += count;
return count;
}
static ssize_t c_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
if (*ppos >= 32)
return 0;
if (*ppos + count > 32)
count = 32 - *ppos;
if (copy_from_user(hello + *ppos, buffer, count))
return -EFAULT;
*ppos += count;
return count;
}
struct file_operations c_fops = {
.owner = THIS_MODULE,
.open = c_open,
.read = c_read,
.write = c_write,
};
debugfs_create_file("c", 0644, sub_dir, NULL, &c_fops);

注:代码里,c_open其实并没有任何用处,因为c_read和c_write直接引用了全局变量hello。这里,我们也可以换一种写法,在read/write函数里用filp->private_data来引用字符数组hello。

到这里,三个文件和子目录已经创建完毕。在module_exit中,我们要记得释放创建的数据。

1
debugfs_remove_recursive(my_debugfs_root);

debugfs_remove_recursive可以帮我们逐步移除每个分配的dentry,如果您想一个一个手动的移除,也可以直接调用debugfs_remove。

附录:

创建和撤销目录及文件

1
2
3
4
5
6
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops);
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);

创建单值文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct dentry *debugfs_create_u8(const char *name, mode_t mode,
struct dentry *parent, u8 *value);
struct dentry *debugfs_create_u16(const char *name, mode_t mode,
struct dentry *parent, u16 *value);
struct dentry *debugfs_create_u32(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
struct dentry *debugfs_create_u64(const char *name, mode_t mode,
struct dentry *parent, u64 *value);
struct dentry *debugfs_create_x8(const char *name, mode_t mode,
struct dentry *parent, u8 *value);
struct dentry *debugfs_create_x16(const char *name, mode_t mode,
struct dentry *parent, u16 *value);
struct dentry *debugfs_create_x32(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
struct dentry *debugfs_create_size_t(const char *name, mode_t mode,
struct dentry *parent, size_t *value);
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
struct dentry *parent, u32 *value);

其中,后缀为x8、x16、x32的这三个函数是指debugfs中的数据用十六进制表示。

创建BLOB文件

1
2
3
4
5
6
7
struct debugfs_blob_wrapper {
void *data;
unsigned long size;
};
struct dentry *debugfs_create_blob(const char *name, mode_t mode,
struct dentry *parent, struct debugfs_blob_wrapper *blob);

其它

1
2
3
4
5
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);



分享到:
评论

相关推荐

    spy:Linux内核模式debugfs键盘记录器

    只要加载模块,按键日志就记录在debugfs中。 只有root或sudoer可以读取日志。 模块名称已伪装为可与其他内核模块混合。 但是,您可以在关机或重新启动时执行脚本(该过程特定于发行版),以将密钥保存到文件中。 ...

    keysniffer:Linux内核模式debugfs键盘记录器-开源

    Linux内核模块,用于捕获键盘或按键记录器中按下的键。 keysniffer最初是在考虑美国键盘(和兼容笔记本电脑)的情况下编写的。 默认情况下,它为按下的键显示人类可读的字符串。 但是,随着键盘的发展,增加了更多的...

    linux内核配置文件说明参考

    linux内核配置文件说明参考 中英文说明 如: Kernel->user space relay support (formerly relayfs) 在某些文件系统上(比如debugfs)提供从内核空间向用户空间传递大量数据的接口 Initramfs source file(s) ...

    debugfs-backdoor:使用debugfs的PoC代码将文件存储在内存中并执行它们

    在内存RCE中滥用debugfs 此仓库包含的代码演示了在可加载内核模块中使用debugfs和netfilter执行代码的方法。 概述 debugfs是一个易于使用的基于RAM的文件系统,专门用于内核调试。 它与2.6.10-rc3版本一起发布,由...

    Netlink实现Linux内核与用户空间通信

    Linux内核与用户空间通信的方式目前主要有9种,分别是内核启动参数、模块参数与 sysfs、sysctl、系统调用、netlink、procfs、seq_file、debugfs和relayfs。Netlink是一种特殊的文件描述符(套结字),为2.6.14及更高...

    Linux 系统内核空间与用户空间通信的实现与分析

    一般地,在使用虚拟内存技术的多任务系统上,内核和应用有不同的地址空间,因此,在内核和应用之间以及在应用与应用之间进行数据交换需要专门的机制来实现,众所周知,进程间通信(IPC)...file、debugfs 和 relayfs。

    LDD-LinuxDeviceDrivers:Linux内核与设备驱动程序学习笔记

    LDD-LinuxDeviceDrivers#1书籍-一些书中的原始码和范例目录书籍#2 study--本人的linux学习以及驱动代码目录书籍学习Linux内核的设计与实现,包括文档和示例代码,文档与同步更新自己实现的一些linux内核的驱动代码...

    vltrace:使用eBPF linux内核功能以快速方式跟踪系统调用的工具

    vltrace是一个系统调用跟踪工具,它利用eBPF(Linux内核的有效跟踪功能)。执照请参阅文件以获取有关此工具如何获得的信息。依赖vltrace取决于库。 libbcc的安装指南可以在找到。系统要求内核v4.7或更高版本 安装的...

    hid-apple-4.1.2:适用于Linux内核4.1.2的Macbook 12,1触控板和键盘调试

    隐藏的苹果一种Linux HID Apple驱动程序,已为2015年初的Apple MacBook Pro 12,1修复,适用于Linux内核4.1.2。 启用在绑定的隐藏式苹果驱动程序上不起作用的“ fn”键,因为它无法识别新的USB驱动程序。 修复无法...

    hid-apple-4.0.x:适用于Linux内核4.0.x的Macbook 12,1触控板和键盘修复

    一个Linux HID Apple驱动程序,已针对2015年初的Apple MacBook Pro 12,1(Linux内核4.0)修复。 启用在绑定的隐藏式苹果驱动程序上不起作用的“ fn”键,因为它无法识别新的USB驱动程序。 修复无法正常工作的触摸...

    hid-apple-3.19:适用于Linux内核3.19.x的Macbook 12,1触控板和键盘调试

    隐藏的苹果适用于Linux内核3.19的2015年初版Apple MacBook Pro 12,1已修复Linux HID Apple驱动程序。 已修补的驱动程序基于发布的修复程序。 修复程序使其成为内核4.2 启用在绑定的隐藏式苹果驱动程序上不起作用的...

    linux-buffer-debug:具有对ext4jbd2的缓冲层跟踪支持Linux

    对ext4 / jbd2(Linux内核2.6.32.67)的缓冲层跟踪支持。 它在bh内记录了针对该bh运行的最后32个BUFFER_TRACE()的输入。 如果J_ASSERT失败,那么您会很好地跟踪此bh发生的最后32次“事件”,包括该过渡时bh的状态。...

    bpf-exercises

    Linux eBPF练习 与eBPF研讨会一起进行的小练习。 要求 装有最新内核(v4.15或更高版本)和DockerLinux系统。 您可以使用“ Vagrant”框,也可以要求我们在云中提供预配置的VM... 要运行此练习,需要挂载内核的debugfs:

    基于Cortex-A9的视频监控系统(华清远见FS4412)

    压缩包中提供了ubuntu20.04 tftp服务器的安装流程和FS4412编译后的内核、设备树、根文件系统以及后期简单的Android应用开发,有简约的页面设计。 FS4412开发板配置过程中有任何问题,可以一起交流!

    Ethercat-Ighmaster.txt

    kernel_source_dir='/mnt/fs_ext/imx6/linux-3.0.35' #内核源码目录 lib_modules_kernel_promt='3.0.35-2666-gbdde708' #内核版本号 host=arm-fsl-linux-gnueabi #交叉编译链前缀 注意要先编译内核,然后编译...

    bpi_a64_android初步编译为LCD显示20170309_1442.7z

    Install system fs image: out/target/product/bpi-m64-lcd/system.img out/target/product/bpi-m64-lcd/system.img+out/target/product/bpi-m64-lcd/obj/PACKAGING/recovery_patch_intermediates/recovery_from_...

    Android文件操作

    modules:使用来存放内核模块(主要是fs和net)和模块配置文件的地方。 framework: 是JAVA平台的一些核心文件,属於JAVA平台系统框架文件。里面的文件都是.jar和.odex文件。 备注:什么是odex文件? odex是被优化过...

    uboott移植实验手册及技术文档

    4、交叉编译器 arm-softfloat-linux-gnu-gcc-3.4.5 【实验步骤】 一、建立自己的平台类型 (1)解压文件 #tar jxvf u-boot-1.3.1.tar.bz2 (2)进入 U-Boot源码目录 #cd u-boot-1.3.1 (3)创建自己的开发板...

Global site tag (gtag.js) - Google Analytics