• linux驱动调试之Debugfs


    本期主题:
    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)
    
    
    • 1
    • 2

    接口描述:

    这是一个创建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)
    
    • 1
    • 2
    • 3

    接口描述:

    在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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.src code

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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");
    
    
    • 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
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156

    3.操作测试

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

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

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

  • 相关阅读:
    盘点一下我用kafka两年以来踩过的一些非比寻常的坑
    8款提高小团队协作效率的app软件,你用过几款?
    @Transactional注解作用,不生效的场景,事务回滚
    微信小程序业务域名保姆级配置教程
    Oracle database 开启归档日志 archivelog
    《数据资产管理实践白皮书》5.0版--数据资产管理保障措施
    asp毕业设计——基于asp+sqlserver的WEB社区论坛设计与实现(毕业论文+程序源码)——社区论坛
    shell编程基础
    【1 操作系统概述】
    【电力系统】基于粒子群算法优化电力系统潮流计算附matlab代码
  • 原文地址:https://blog.csdn.net/weixin_37620587/article/details/126091192