首页 > 系统相关 >linux内核 DebugFS

linux内核 DebugFS

时间:2023-01-02 22:08:45浏览次数:47  
标签:struct dentry debugfs create DebugFS char 内核 linux mode


一、简介

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

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

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

mount -t debugfs none /sys/kernel/debug


二、打开debugfs选项

要使用debugfs,首先我们要设置一下配置选项CONFIG_DEBUG_FS,可以在config文件中设置CONFIG_DEBUG_FS=y,也可以通过menuconfig来设置

Kernelhacking --->

                          [*]Debug Filesystem

并且驱动中使用debugfs需要包含头文件<linux/debugfs.h>,为了在用户态下使用debugfs,必须把它mount到一个目录下


三、Llinux内核为debugfs提供了非常简洁的API:

​debugfs
|--mydebug​
​​
   |--subdir​
​​
      c​
​​
   a​
​    b​


1、创建和撤销目录及文件



​1)struct​​​​dentry *debugfs_create_dir(​​​​const​​​​char​​​​*name,​​​​struct​​​​dentry *parent);
第一个参数是目录的名称,第二个参数是指定这个目录的上级目录,如果是NULL,表示放在debugfs的根目录下
my_debugfs_root = debugfs_create_dir("mydebug", NULL);
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);
debugfs
|--mydebug
   |--subdir



​2)struct​​​​dentry *debugfs_create_file(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent,​​​​void​​​​*data,​​​​const​​​​struct​​​​file_operations *fops);
文件c通过自定义的文件操作实现读写
debugfs
|--mydebug
   |--subdir
      c

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




​3)void​​​​debugfs_remove(​​​​struct​​​​dentry *dentry);​



​4)void​​​​debugfs_remove_recursive(​​​​struct​​​ ​​​dentry *dentry);
在module_exit中,我们要释放创建的数据
debugfs_remove_recursive(my_debugfs_root);
这个函数可以帮我们逐步移除每个分配的dentry,如果你想要一个个手动移除,也可以直接调用debugfs_remove

2、创建单值文件

​1)struct​​​​dentry *debugfs_create_u8(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u8 *value);
debugfs_create_u8("a", 0644, my_debugfs_root, &a);
debugfs
|--mydebug
   |--subdir
   a
创建的文件名是a,对应内核中的变量名为a,文件属性为0644,可以对文件读写,相当于是对内核变量读写。



​2)struct​​​​dentry *debugfs_create_u16(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u16 *value);​



​3)struct​​​​dentry *debugfs_create_u32(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u32 *value);​



​4)struct​​​​dentry *debugfs_create_u64(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u64 *value);​



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



​5)struct​​​​dentry *debugfs_create_x8(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u8 *value);​



​6)struct​​​​dentry *debugfs_create_x16(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u16 *value);​



​7)struct​​​​dentry *debugfs_create_x32(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u32 *value);​



​8)struct​​​​dentry *debugfs_create_size_t(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent,​​​​size_t​​​​*value);​



​9)struct​​​​dentry *debugfs_create_bool(​​​​const​​​​char​​​​*name, mode_t mode,​​​​struct​​​​dentry *parent, u32 *value);

3、创建BLOB文件

​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);
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);
debugfs
|--mydebug
   |--subdir
   b
需要注意的是blob wrapper定义的数据只能是只读的,在本例中我们将文件b的权限设定为0644,但实际这个文件还是只读的,
如果试图改写这个文件,系统将提示出错。
因此,如果我们要对内核数组进行读写动作,blob wrapper就无法满足要求,我们只能自己定义文件操作来实现。

4、其他

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

以上部分转自:http://www.cnblogs.com/wwang/archive/2011/01/17/1937609.html


三、实例
这个实例虽然简单,但是融合了如何在debugfs目录下创建文件,并给出了文件的操作方法;
也说明了如何创建变量文件,并且用户空间读写这个变量文件相当于是在读写内核空间的这个文件对应的变量

// dbgfs.c 
/*
* (C) 05-07-2012 Yang Honggang (Joseph), Dslab <[email protected]>
*/
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/uaccess.h>

struct dentry *parent, *sw, *inf;
u32 tr_on = 0;

struct my_data_struct {
void* data;
unsigned long size;
} mds;

struct page* pg;

static ssize_t data_read(struct file *file, char __user *user_buf,size_t count, loff_t *ppos)
{
unsigned long i;
size_t cnt;

printk("tr_on:%d\n", tr_on);
/* If the tracing_on is Y, fill the data buffer with debug info */
if (tr_on) {
tr_on = 0; /* Automaticlly clear the 'tracing_on' flag */
for (i = 0; i < (mds.size - 11) / 11; i ++) {
sprintf((char*)((char*)mds.data + i * 11 ), "%ld\n", i + 1000000000);
}

/* Copy debug info to userspace */
cnt = copy_to_user(user_buf, (const void *)mds.data, mds.size);
return (mds.size - cnt);
}

return 0;
}

const struct file_operations fops =
{
.read = data_read,
};

static int __init dbgfs_demon_init(void)
{

printk("dbgfs init\n");
/* Create dbgfs_demon directory in the debugfs root */
parent = debugfs_create_dir("dbgfs_demon", NULL);
if (!parent)
return -1;

//在debugfs目录下创建变量名tracing_on,对应内核中的变量tr_on
/* Create a output switch in dbgfs_demon */
sw = debugfs_create_bool("tracing_on", S_IRWXU,parent, &tr_on);
if (!sw)
goto p_out;

/* Create a file for debug information exporting */
mds.size = 4 * 1024;
/* Allocate one page for info. storing */
pg = alloc_pages(__GFP_HIGHMEM | __GFP_ZERO, 0);
/* Covert to Memory address */
mds.data = (void*) page_address(pg);
if (!pg)
goto p_out;

//在debugfs目录下创建文件名data,对应的文件操作为fops里面的read和write方法,
//通过read函数中从用户空间传来的buf进行相应的处理。
inf = debugfs_create_file("data", S_IRUSR,parent, &mds, &fops);
if (!inf)
goto sw_out;

return 0;

sw_out:
debugfs_remove(sw);
__free_pages(pg, 0);
p_out:
debugfs_remove(parent);

return -1;
}

static void __exit dbgfs_demon_exit(void)
{

if (pg)
__free_pages(pg, 0);

debugfs_remove(inf);
debugfs_remove(sw);
debugfs_remove(parent);
printk("dbgfs exit\n");

return;
}

module_init(dbgfs_demon_init);
module_exit(dbgfs_demon_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yang Honggang (Joseph) <[email protected]>");

模块会在/sys/kernel/debugfs根目录下创建

dbgfs_demon/

|--data

|--tracing_on

函数的操作逻辑是,如果tracing_on的值为Y,那么可以从文件data中读出有用的调试信息,

如果为N,那么读data操作将不会返回任何数据。




















标签:struct,dentry,debugfs,create,DebugFS,char,内核,linux,mode
From: https://blog.51cto.com/u_15854579/5984196

相关文章

  • Linux下安装Anaconda3,这个教程一定要看!
    前言大家好,我是爱写Bug的麦洛。由于工作需要,要为客户搭建Python开发环境。作为从来没有接触过Python的小白,为了完成任务,也是破费周折,请教了身边做Python的朋友,发现大家都是......
  • Linux常用命令
    Linux常用命令 1.cd切换目录菜单~切换到家目录cd/切换到根目录cd-切换到上一次操作的目录cd..或cd../切换到上级目录cd.或cd./切换......
  • SSDT Hook—— 本质上和inline hook没有区别,无非是在内核层面而已!注意Windows Vista X
    SSDTHookSSDTHook属于内核层Hook,也是最底层的Hook。由于用户层的API最后实质也是调用内核API(Kernel32->Ntdll->Ntoskrnl),所以该Hook方法最为强大。不过值得注意的是http......
  • linux 找回root密码方法(CentOs 7.6)
    1:首先,启动系统,进入开机界面,在界面中按e进入编辑界面。如下图:2:进入编辑界面,使用键盘上的上下间吧光标往下移动,找到以"linux16"开头内容所在的行数,在行的最后面输......
  • linux led flash驱动分析
    led作为嵌入式设备中常见的器件,特别是在手机上,一般手机现在都会配置1-2led灯,用于camera补光和手电筒照明功能。led驱动是字符设备,数据流和操作比较简单。闪光灯的几种模......
  • Linux基础命令 ls 的使用
    ls作用是:显示指定目录下的文件和属性信息。我列出几种常用的 以上我写了三条命令,还有很多选项感兴趣的可以通过  https://www.linuxcool.com/ls 去查看了解第......
  • c语言获取当前工作路径的实现代码(windows/linux)
    https://www.php1.cn/detail/c_YuYanHuoQuDang_c0079976.html Linux函数名:getcwd功能:取得当前的工作目录用法:char*getcwd(char*buf,size_tsize);函数......
  • Windows 恶意软件数量是 Mac 的 5000 倍,是 Linux 的 36 倍
    AV-TEST是一个独立的测试机构,他们会根据各种标准对操作系统的防病毒和安全软件进行评估和评级,并将测试结果免费提供给用户,帮助用户选择最适合自己的产品。近日,AV-TEST联......
  • linux环境搭建
    操作系统:Ubuntu查看主机ip:ifconfig若用不了该指令,先安装net-toolssudoaptinstallnet-tools然后用ifconfig得到主机ip2.虚拟机与Xshell连接虚拟机上先安装openss......
  • 极度简约 最小 Linux 发行版 Tiny Core Linux 7.1 发布
    感谢​​LinuxStory​​的投递TinyCoreLinux是一个极度简约但是也高度可扩展的GNU/Linux发行版,其之精简甚至可以小到只有10MB大小,昨天5月23日刚刚发布的TinyCore......