当前位置: 首页 > news >正文

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);

 

 

 



作者:wwang 
出处:http://www.cnblogs.com/wwang 
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

转载于:https://www.cnblogs.com/zengkefu/p/5639589.html

相关文章:

  • Linux 必掌握的 SQL 命令
  • curl的使用(from 阮一峰)
  • 对接微信红包时:CA证书出错,请登录微信支付商户平台下载证书
  • 深入理解JAVA I/O系列一:File
  • Java中的一些方法
  • Ubuntu server 16.04 安装mysql并设置远程访问
  • mybatis源码学习: 编译的方法
  • iOS 主动抛出异常
  • A Game
  • 选择最适合你的Linux学习方法
  • 什么是GPS的冷启动、温启动和热启动?
  • Clone
  • 正则表达式学习
  • 2205 Problem B
  • [React]全自动数据表格组件——BodeGrid
  • 【RocksDB】TransactionDB源码分析
  • CSS盒模型深入
  • iOS 系统授权开发
  • IP路由与转发
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • log4j2输出到kafka
  • miaov-React 最佳入门
  • Nodejs和JavaWeb协助开发
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • uni-app项目数字滚动
  • webpack项目中使用grunt监听文件变动自动打包编译
  • WebSocket使用
  • windows下mongoDB的环境配置
  • - 概述 - 《设计模式(极简c++版)》
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 用mpvue开发微信小程序
  • mysql面试题分组并合并列
  • #100天计划# 2013年9月29日
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (zt)最盛行的警世狂言(爆笑)
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • 、写入Shellcode到注册表上线
  • .net core 依赖注入的基本用发
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .NET开发不可不知、不可不用的辅助类(一)
  • @Transactional类内部访问失效原因详解
  • [ MSF使用实例 ] 利用永恒之蓝(MS17-010)漏洞导致windows靶机蓝屏并获取靶机权限
  • [【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器
  • [acwing周赛复盘] 第 69 场周赛20220917
  • [APUE]进程关系(下)
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [BZOJ1089][SCOI2003]严格n元树(递推+高精度)
  • [C]编译和预处理详解
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [Lua实战]整理Lua中忽略的问题
  • [NAND Flash 7.1] 闪存系统性能优化方向集锦?AC timing? Cache? 多路并发?
  • [Node + Docker] 聊聊怎么把 nodeclub 构建成 Docker 镜像
  • [NOIP2018 PJ T4]对称二叉树