热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linuxdebugfs根目录,LinuxDebugfs文件系统

1Debugfs简介Debugfs文件系统目的是为开发人员提供更多内核数据,方便调试内容.我们知道proc文件系统关注的是进程信息,sysfs关注是one-value

1 Debugfs简介

Debugfs文件系统目的是为开发人员提供更多内核数据,方便调试内容. 我们知道/proc文件系统关注的是进程信息,/sysfs关注是one-value-per-file策略集,而Debugfs文件系统没有如此多限制,可是任何内核要输出的信息。

2 Debugfs使用

2.1 安装文件系统

Debugfs没有物理设备,其挂载方式:

mount -t debugfs none /sys/kernel/debug

2.2 编程使用

2.2.1 要使用Debugfs的模块需要包含

2.2.2 具体步骤

a 创建目录。所有该模块输出的信息都在该目录下面。

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);

b 创建文件。在上面创建的目录下面创建文件。

struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data,

const struct file_operations *fops);

通过fops操作文件的读写等请求,文件一般用来传递字符创等信息。

c 对于数字情况下,就没有必须通过文件来传递信息了。Debugfs提供了针对数字的接口:

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_bool(const char *name, mode_t mode, struct dentry *parent, u32 *value);

struct dentry *debugfs_create_blob(const char *name, mode_t mode, struct dentry *parent,

struct debugfs_blob_wrapper *blob);

d 模块卸载,需要主动删除目录下的文件。

void debugfs_remove_recursive(struct dentry *dentry);

3 Debugfs内核代码分析

3.1 初始化和释放资源

static int __init debugfs_init(void)

{

int retval;

debug_kobj = kobject_create_and_add("debug", kernel_kobj);

if (!debug_kobj)

return -EINVAL;

retval = register_filesystem(&debug_fs_type);

if (retval)

kobject_put(debug_kobj);

else

debugfs_registered = true;

return retval;

}

static void __exit debugfs_exit(void)

{

debugfs_registered = false;

simple_release_fs(&debugfs_mount, &debugfs_mount_count);

unregister_filesystem(&debug_fs_type);

kobject_put(debug_kobj);

}

3.2 关键函数

debugfs_create_file函数是关键函数,其他接口基本都是该接口的wrapper。

3.2.1 变参数宏的定义

#define pr_debug(fmt, ...) do { \

dynamic_pr_debug(fmt, ##__VA_ARGS__); \

} while (0)

dynamic_pr_debug 也是变参数的.

3.2.2 debugfs_create_file

static struct vfsmount *debugfs_mount;

static int debugfs_mount_count;

static bool debugfs_registered;

struct debugfs_blob_wrapper {

void *data;

unsigned long size;

};

static struct file_system_type debug_fs_type = {

.owner = THIS_MODULE,

.name = "debugfs",

.get_sb = debug_get_sb,

.kill_sb = kill_litter_super,

};

struct dentry *debugfs_create_file(const char *name, mode_t mode,

struct dentry *parent, void *data,

const struct file_operations *fops)

{

struct dentry *dentry = NULL;

int error;

pr_debug("debugfs: creating file '%s'\n",name);

error = simple_pin_fs(&debug_fs_type, &debugfs_mount,

&debugfs_mount_count);

if (error)

goto exit;

error = debugfs_create_by_name(name, mode, parent, &dentry);

if (error) {

dentry = NULL;

simple_release_fs(&debugfs_mount, &debugfs_mount_count);

goto exit;

}

if (dentry->d_inode) {

if (data)

dentry->d_inode->i_private = data;

if (fops)

dentry->d_inode->i_fop = fops;

}

exit:

return dentry;

}

int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count)

{

struct vfsmount *mnt = NULL;

spin_lock(&pin_fs_lock);

if (unlikely(!*mount)) {

spin_unlock(&pin_fs_lock);

mnt = vfs_kern_mount(type, 0, type->name, NULL);

if (IS_ERR(mnt))

return PTR_ERR(mnt);

spin_lock(&pin_fs_lock);

if (!*mount)

*mount = mnt;

}

mntget(*mount);

++*count;

spin_unlock(&pin_fs_lock);

mntput(mnt);

return 0;

}

static int debugfs_create_by_name(const char *name, mode_t mode,

struct dentry *parent,

struct dentry **dentry)

{

int error = 0;

if (!parent) {

if (debugfs_mount && debugfs_mount->mnt_sb) {

parent = debugfs_mount->mnt_sb->s_root;

}

}

if (!parent) {

pr_debug("debugfs: Ah! can not find a parent!\n");

return -EFAULT;

}

*dentry = NULL;

mutex_lock(&parent->d_inode->i_mutex);

*dentry = lookup_one_len(name, parent, strlen(name));

if (!IS_ERR(*dentry)) {

switch (mode & S_IFMT) {

case S_IFDIR:

error = debugfs_mkdir(parent->d_inode, *dentry, mode);

break;

case S_IFLNK:

error = debugfs_link(parent->d_inode, *dentry, mode);

break;

default:

error = debugfs_create(parent->d_inode, *dentry, mode);

break;

}

dput(*dentry);

} else

error = PTR_ERR(*dentry);

mutex_unlock(&parent->d_inode->i_mutex);

return error;

}

static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev)

{

struct inode *inode = new_inode(sb);

if (inode) {

inode->i_mode = mode;

inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;

switch (mode & S_IFMT) {

default:

init_special_inode(inode, mode, dev);

break;

case S_IFREG:

inode->i_fop = &debugfs_file_operations;

break;

case S_IFLNK:

inode->i_op = &debugfs_link_operations;

break;

case S_IFDIR:

inode->i_op = &simple_dir_inode_operations;

inode->i_fop = &simple_dir_operations;

inc_nlink(inode);

break;

}

}

return inode;

}

const struct file_operations debugfs_file_operatiOns= {

.read = default_read_file,

.write = default_write_file,

.open = default_open,

};

const struct file_operations simple_dir_operatiOns= {

.open = dcache_dir_open,

.release = dcache_dir_close,

.llseek = dcache_dir_lseek,

.read = generic_read_dir,

.readdir = dcache_readdir,

.fsync = simple_sync_file,

};

const struct inode_operations simple_dir_inode_operatiOns= {

.lookup = simple_lookup,

};

3.3 其他函数

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)

{

return debugfs_create_file(name,

S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,

parent, NULL, NULL);

}

struct dentry *debugfs_create_bool(const char *name, mode_t mode,

struct dentry *parent, u32 *value)

{

return debugfs_create_file(name, mode, parent, value, &fops_bool);

}

由以上可以看出,其他的输出信息都是debugfs_create_file的包装,这里主要是debugfs_create_file函数最后一个参数struct file_operations不同而已。

这里以bool类型为例:

static ssize_t read_file_bool(struct file *file, char __user *user_buf,

size_t count, loff_t *ppos)

{

char buf[3];

u32 *val = file->private_data;

if (*val)

buf[0] = 'Y';

else

buf[0] = 'N';

buf[1] = '\n';

buf[2] = 0x00;

return simple_read_from_buffer(user_buf, count, ppos, buf, 2);

}

static ssize_t write_file_bool(struct file *file, const char __user *user_buf,

size_t count, loff_t *ppos)

{

char buf[32];

int buf_size;

u32 *val = file->private_data;

buf_size = min(count, (sizeof(buf)-1));

if (copy_from_user(buf, user_buf, buf_size))

return -EFAULT;

switch (buf[0]) {

case 'y':

case 'Y':

case '1':

*val = 1;

break;

case 'n':

case 'N':

case '0':

*val = 0;

break;

}

return count;

}

static const struct file_operations fops_bool = {

.read = read_file_bool,

.write = write_file_bool,

.open = default_open,

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之下,如果您的发行版里没有自动挂载,可以用如下命令手动完成:

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

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

2c96dd585300bd31c75fe956d49356a5.png

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

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

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

子目录也是用debugfs_create_dir来实现:

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

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

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

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

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

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

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

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

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

附录:

创建和撤销目录及文件

创建单值文件

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

创建BLOB文件

其它



推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
author-avatar
hhha老窝_349
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有