Coding: 小写一个debugfs
2024-07-05 08:56:16 作者:佚名 上一个月我写了一个简单的module,熟悉了module的挂载、查看和卸载。这一次我们将使用linux的debugfs API编写一个调试文件系统。
底层的API已经全部编写好了,我们只需要简单地调用API即可完成。
事先检查
首先检查当前的内核是否支持debugfs调试:
zcat /proc/config.gz | grep DEBUG_FS
# CONFIG_XEN_DEBUG_FS is not set
CONFIG_BLK_DEBUG_FS=y
CONFIG_BLK_DEBUG_FS_ZONED=y
# CONFIG_SCSI_SNIC_DEBUG_FS is not set
# CONFIG_SCSI_LPFC_DEBUG_FS is not set
# CONFIG_USB_GADGET_DEBUG_FS is not set
# CONFIG_OCFS2_DEBUG_FS is not set
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_FS_ALLOW_ALL=y
# CONFIG_DEBUG_FS_DISalloW_MOUNT is not set
# CONFIG_DEBUG_FS_ALLOW_NONE is not set
在这里,我们需要查看的是:
CONFIG_DEBUG_FS=y
, 如果是n,说明当前内核不支持调试文件系统,需要自定义内核。如果需要帮助,可以阅读之前我写的博客,在Arch Linux上编写自定义内核模块。
开始
文件系统有自己一套fops。与我们对文件系统的理解一样,文件系统提供了对设备的一套抽象访问读写等操作的句柄。因此,我们需要实现这些基本操作,如打开文件、读文件和写文件。
文件系统将以模块的方式动态加载到内核中。所以我们需要首先学习如何编写模块,并掌握相关知识。如果您之前不熟悉这方面的知识,可以阅读我之前的博客,先掌握这些内容,然后进行实践。
现在,让我们继续编写模块的Makefile:
obj-m:= charlie.o
pwd:= $(shell pwd)
ker-ver:= $(shell uname -r)
KDIR:= /lib/modules/$(ker-ver)/build
all:
make -C $(KDIR) M=$(pwd) modules
clean:
rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions *.order *.symvers *.mod写代码!
撸代码
我们首先需要引入写模块和调试文件系统的基本头文件。
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/types.h>
然后完成读写操作和打开文件的基本操作。
static int charlie_fs_open(struct inode* inode, struct file* pfile)
{
printk("Charlie_filesystem_open\n");
pfile->private_data = inode->i_private;
return 0;
}
static sSize_t charlie_fs_read(struct file* pFile, char __user *buf, size_t cnt, loff_t* offp)
{
int retval = 0;
if((*offp + cnt) > 512)
cnt = 512 - *offp;
printk("Received read request! count:%ld, offset:%lld\n", cnt, *offp);
if(copy_to_user(buf, charlie_buf + *offp, cnt)){
pr_warn("Oh no, failed to copy to user! count is %ld\n", cnt);
retval = -EFAULT;
goto out;
}
*offp += cnt;
retval = cnt;
out:
return retval;
}
static ssize_t charlie_fs_write(struct file* pFile, const char __user *buf, size_t cnt, loff_t* offp)
{
int retval;
pr_info("Write request is here: count: %ld, offset:%lld\n", cnt, *offp);
if(*offp > 512)
return 0;
if((*offp + cnt) > 512)
cnt = 512 - *offp;
if(copy_from_user(charlie_buf + *offp, (const void*)buf, cnt)){
pr_warn("Oh no, failed to copy from user! count is %ld\n", cnt);
retval = -EFAULT;
goto out;
}
*offp += cnt;
retval = cnt;
out:
return retval;
}
关于这里使用的函数的详细说明,可以自行查找更多信息。
文件系统是通过模块进行载入和卸载的,这意味着我们需要编写初始化函数和析构函数。我们需要在初始化时完成文件系统处理函数的注册,并在卸载文件系统时移除在初始化时注册的相关函数。
struct file_operations charlie_fs_fops = {
.owner = THIS_MODULE,
.read = charlie_fs_read,
.write = charlie_fs_write,
.open = charlie_fs_open
};
static int __init charlie_debug_fs_init(void)
{
pr_info("The module is initing...");
charlie_dir = debugfs_create_dir("Charliedir", NULL);
if(!charlie_dir){
pr_crit("Failing shit! can not create any dir at all!");
goto failed;
}
static struct dentry* sub_charlie_dir;
sub_charlie_dir = debugfs_create_dir("CharlieSubDir", charlie_dir);
if(!sub_charlie_dir){
pr_crit("Failing shit! can not create any sub dir at all!");
goto failed;
}
struct dentry* filent = debugfs_create_file("Charlie", 0644, sub_charlie_dir, NULL, &charlie_fs_fops);
if(!filent){
pr_err("Can not create file!");
goto failed;
}
pr_info("Init finish!");
return 0;
failed:
return -ENOENT;
}
static void __exit charlie_debug_fs_exit(void)
{
pr_info("Safe quit! begin");
debugfs_remove_recursive(charlie_dir);
pr_info("Safe quit! end");
}
module_init(charlie_debug_fs_init);
module_exit(charlie_debug_fs_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Charliechen");
保存并进行编译命令:
make -C /lib/modules/6.9.7-arch1-1/build M=/home/Charliechen/Works/opearte_system/module/test2 modules
sudo insmod charlie.ko && lsmod | grep charlie
模块正确挂载后,下一步是测试我们编写的功能。
sudo ls /sys/kernel/debug/ | grep Charliedir
sudo ls /sys/kernel/debug/Charliedir
sudo ls /sys/kernel/debug/Charliedir/CharlieSubDir
sudo echo 114514 > /sys/kernel/debug/Charliedir/CharlieSubDir/Charlie
测试过程已经完成。