本期主题:
linux驱动调试之Debugfs
往期链接:
- linux设备驱动中的并发
- linux设备驱动中的编译乱序和执行乱序
- linux设备驱动之内核模块
- linux字符驱动
- linux字符驱动之ioctl部分
- linux字符驱动之read、write部分
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.操作测试
文章来源:https://www.toymoban.com/news/detail-634660.html
4.关于cat会一直读取的原因
这个问题可以查看链接 使用cat读取和echo写内核文件节点的一些问题 文章来源地址https://www.toymoban.com/news/detail-634660.html
到了这里,关于linux驱动调试之Debugfs的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!