本期主题:
linux驱动调试之Debugfs


往期链接:



1.Debugfs是什么

在内核的开发过程中,我们希望有一些能够在用户空间获取信息的简单方法。debugfs由此诞生,我们可以在 /sys/kernel/debug 目录下来访问debugfs的节点。

debugfs与proc、sysfs不同

  • proc提供的是进程信息,sysfs具有严格的“每个文件一个值“的规则
  • debugfs开发比较随意,从名字就能看出来

2.怎么使用debugfs

1.debugfs_create_dir

接口定义:

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

接口描述:

这是一个创建debugfs目录的接口,其中:

  • name就是你希望创建的文件夹的名字;
  • parent就是这个debug节点的父节点,如果为NULL的话,就会在debugfs的根目录下创建;

2.debugfs_create_file

接口定义:

struct dentry *debugfs_create_file(const char *name, umode_t mode,
				   struct dentry *parent, void *data,
				   const struct file_operations *fops)

接口描述:

在debugfs目录下创建debugfs文件的接口,其中:

  • name 就是创建文件的名字,mode就是希望文件所拥有的权限
  • parent和前一个接口一样
  • data 一个可以存储数据的指针,inode.i_private 指针将会指向这个值,当调用open时候
  • fops就是存储操作的函数指针

3.一个简单的例子

基于我们前面调试的hello模块,继续创建一个debugfs节点,并且读取它

1.Makefile

CONFIG_MODULE_SIG=n

ifneq ($(KERNELRELEASE),)

obj-m+=myhello.o

myhello-objs:=hello.o hello_dbgfs.o #将多个文件编译成一个目标的方法

else

KDIR :=/lib/modules/$(shell uname -r)/build

PWD  :=$(shell pwd)

all:

	make -C $(KDIR) M=$(PWD) modules

clean:

	rm -f *.ko *.o *.mod.o *.symvers *.cmd  *.mod.c *.order .*.cmd

endif

2.src code

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include "hello_dbgfs.h"

struct hello_dbgfs_dentry_t {
    /* for debugfs */
	struct dentry *dentry_root;
	struct dentry *dentry_file;
};

static struct hello_dbgfs_dentry_t hello_dbgfs_dentry;

int hello_dbgfs_open(struct inode *inode, struct file *file)
{
	if (inode->i_private)
		file->private_data = inode->i_private;
	return 0;
}

static ssize_t hello_dbgfs_read(struct file *file, char __user *user_buf,
				     size_t count, loff_t *ppos)
{
	char buf[] = "hello world\n";;
	ssize_t ret;
    unsigned long p = *ppos;
	size_t size = sizeof(buf) / sizeof(char);

    printk("read debug\n");
    printk("pos is %ld, count is %d, size: %d\n", p, count, size);

//TODO:这里如果不加这个判断的话,一次cat会多次打印,原因在于cat返回的是读取的字节数,如果没达到预期会一直读取

	if (p > size)
	{
		return 0;
	}

	// buf = kmalloc(TEST_SIZE, GFP_KERNEL);
	if (!buf) {
		printk("ERR, buf is NULL!\n");
		return -ENOMEM;
	}

	// if (ret >= 0)
		// ret = simple_read_from_buffer(user_buf, 10, ppos, buf, ret);
	if (copy_to_user(user_buf, buf, size))
	{
		ret = -EFAULT;
		printk("ERR, debugfs read failed!\n");
	}
	else
	{
		//TODO:如果没有return这个size,在cat之后会报错Bad address
		ret = size;
		*ppos += size;
		printk("read %u bytes from kernel\r\n", size);
	}
	
	// kfree(buf);

	return ret;

}

static ssize_t hello_dbgfs_write(struct file *file,
				      const char __user *user_buf, size_t count,
				      loff_t *ppos)
{
	char *buf_recv = NULL;
	int ret;
    // char *buf = (char *)file->private_data;
    if (user_buf == NULL)
    {
        printk("ERR, user buf is NULL!\n");
        return 0;
    }

    printk("DBG, dbgfs write, count is %d\n", count);
	
	buf_recv = (char *)kmalloc(count, GFP_KERNEL);
	if (buf_recv == NULL) {
		printk("ERR, kmalloc failed!");
		return -1;
	}

    if (copy_from_user(buf_recv, user_buf, count))
    {
        ret = -EFAULT;
        printk("write failed!\n");
    }
    else
    {
        *ppos += count;
        ret = count;

        printk("write %d bytes to kernel, buf_recv is %s\n", count, buf_recv);
    }
//最后一个字符是换行符,而不是string结尾的0,这里很奇怪
	if (buf_recv[count - 1] == '\n') {
		printk("INFO, delete the n, change to 0 \n");
		buf_recv[count - 1] = 0;
	}

	if (!strcmp(buf_recv, "write"))
		printk("INFO, use write func\n");
	else if (!strcmp(buf_recv, "poll"))
		printk("INFO, use poll func\n");
	else
		printk("ERR, not find the right func, check param!\n");


	kfree(buf_recv);

    return ret;
}


static const struct file_operations hello_dbgfs_ops = {
	.open = hello_dbgfs_open,
	.read = hello_dbgfs_read,
	.write = hello_dbgfs_write,
};

int hello_init_debugfs(void)
{
	hello_dbgfs_dentry.dentry_root = debugfs_create_dir("hello_dbgfs", NULL);
	if (!hello_dbgfs_dentry.dentry_root) {
		printk("Failed to create debugfs directory\n");
		return -1;
	}

	hello_dbgfs_dentry.dentry_file = debugfs_create_file("hello_test", 0644,
						  hello_dbgfs_dentry.dentry_root,
						  NULL, &hello_dbgfs_ops);
	if (!hello_dbgfs_dentry.dentry_file) {
		printk("Failed to create dentry_file\n");
        return -1;
    }
    return 0;
}
// EXPORT_SYMBOL(hello_init_debugfs);

void hello_uninit_debugfs(void)
{
	debugfs_remove_recursive(hello_dbgfs_dentry.dentry_root);
}


MODULE_LICENSE("GPL");

3.操作测试

在这里插入图片描述
在这里插入图片描述

4.关于cat会一直读取的原因

这个问题可以查看链接 使用cat读取和echo写内核文件节点的一些问题

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐