内核实验(六):使用misc框架,实现简单字符设备驱动
一、篇头
使用自行组建的Qemu Linux虚拟机,提升效率,继续内核实验。本文将学习使用misc框架来创建设备驱动。
在linux系统中,主设备号,在历史的长河里,都是固定分配好的(见内核文档 Documentation/admin-guide/devices.txt)。对于其他研究内核驱动,或编写驱动的开发人员来说,必须保证自己所使用的主设备号不与现有设备号发生冲突。方法一,自然是使用内核实验(五)中所使用的动态分配接口。其次,就是使用MISC设备驱动框架,它为这些小设备提供了一个公用的主设备号,此设备号则依据申请分配。
二、系列文章
略……
三、实验环境
- 编译服务器+NFS:ubuntu 22.04
- Qemu 虚拟机:Linux version 5.15.102 + Buysbox 1.3.36 + ARM_32bit
- Qemu 启动命令:qemu-system-arm -nographic -M vexpress-a9 -m 1024M -kernel arch/arm/boot/zImage -initrd …/busybox/rootfs.ext4.img.gz -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb
四、源码解析
4.1 app源码
- 文件名:linux-stable\my_kmodules\app_test.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define MY_DEV_NAME "/dev/my_dev"int main()
{char buffer[64];int fd;fd = open(MY_DEV_NAME, O_RDONLY);if (fd < 0) {printf("open device %s failded\n", MY_DEV_NAME);return -1;}read(fd, buffer, 64);close(fd);return 0;
}
4.2 驱动源码
- 文件名:linux-stable\my_kmodules\test_4.c
4.2.1 关键部分
/*
* (1)使用MISC_DYNAMIC_MINOR动态分配次设备号;而主设备号则自动使用MISC框架的主设备号10;
* (2)创建MISC设备结构体 struct miscdevice test_4_misc_device;
* (3)使用misc_register() 注册MISC框架设备驱动;使用 misc_deregister()注销。
*/
static struct miscdevice test_4_misc_device ={.minor = MISC_DYNAMIC_MINOR,.name = MY_DEV_NAME,.fops = &test_fops,
};
4.2.2 完整源码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>#define MY_DEV_NAME "my_dev"static int test_4_open(struct inode *inode, struct file *file)
{int major = MAJOR(inode->i_rdev);int minor = MINOR(inode->i_rdev);pr_info("%s: major=%d, minor=%d\n", __func__, major, minor);return 0;
}static int test_4_release(struct inode *inode, struct file *file)
{pr_info("%s \n", __func__);return 0;
}static ssize_t test_4_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{pr_info("%s \n", __func__);return 0;
}static ssize_t test_4_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{pr_info("%s \n", __func__);return 0;}static const struct file_operations test_fops = {.owner = THIS_MODULE,.open = test_4_open,.release = test_4_release,.read = test_4_read,.write = test_4_write
};static struct miscdevice test_4_misc_device ={.minor = MISC_DYNAMIC_MINOR,.name = MY_DEV_NAME,.fops = &test_fops,
};static int __init test_4_init(void)
{int ret;pr_info("test_4_init\n");ret = misc_register(&test_4_misc_device);if (ret != 0 ) {pr_err("failed to misc_register");return ret;}pr_err("Minor number = %d\n", test_4_misc_device.minor);return 0;
}static void __exit test_4_exit(void)
{pr_info("test_4_exit\n");misc_deregister(&test_4_misc_device);
}module_init(test_4_init);
module_exit(test_4_exit);MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("test_4, 使用misc框架开发设备驱动");
4.3 Makefile
- 文件名:linux-stable\my_kmodules\Makefile
- 本实验,继承之前的做法,只单独添加test_4.o即可
KDIR := /home/szhou/works/qemu_linux/linux-stableobj-m := test_1.o test_2.o test_3.o test_4.o
all :$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) cleanrm -f *.ko
五、编译及部署
(1)执行驱动KO编译
shou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ make
make -C /home/szhou/works/qemu_linux/linux-stable M=/home/szhou/works/qemu_linux/linux-stable/my_kmodules modules
make[1]: Entering directory '/home/szhou/works/qemu_linux/linux-stable'CC [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_4.oMODPOST /home/szhou/works/qemu_linux/linux-stable/my_kmodules/Module.symversLD [M] /home/szhou/works/qemu_linux/linux-stable/my_kmodules/test_4.ko
make[1]: Leaving directory '/home/szhou/works/qemu_linux/linux-stable'
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$(2)编译APP,采用 --staitc 静态链接
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ arm-linux-gnueabi-gcc app_test.c -o app_test --static(3)将KO和APP存放到NFS共享目录
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp test_4.ko ~/works/nfs_share/
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$ cp app_test ~/works/nfs_share/
szhou@bc01:~/works/qemu_linux/linux-stable/my_kmodules$
六、运行及测试
(1)启动之前编译组建的QEMU虚拟机
Please press Enter to activate this console. (2)挂载NFS共享目录
~ # mount -t nfs -o nolock 192.168.3.67:/home/szhou/works/nfs_share /mnt(3)查看设备文件,这时候还没加载驱动,也没创建节点,所以肯定是没有的
~ # ls /dev | grep my
~ # ls /sys/class/misc | grep my(4) 加载ko
~ # cd /mnt/
/mnt # insmod test_4.ko
test_4: loading out-of-tree module taints kernel.
test_4_init
Minor number = 125(4)查看设备文件,可见/dev/my_dev 依旧是不存在的(这是因为/dev下的设备并非由驱动创建,而是由/sbin/mdev -s,此处我们就手动创建就行)
/mnt # ls /dev | grep my(5)查看misc设备,可见已创建 /sys/class/misc/my_dev, 但这是设备目录文件,非设备的用户接口
/mnt # ls /sys/class/misc | grep my
my_dev(6)手动创建设备文件,主设备号为MISC框架的设备号:10 (日常开发misc,会自动调用 /sbin/mdev -s创建设备,此处虽然已经配置,但我的hotplug还是有问题,暂时用手动创建了)
/mnt # mdev -s (会检查/sys/class下所有dev文件,并创建/dev/xxx),效果等同于下面这个命令
(# mknod /dev/my_dev c 10 125 )(7)运行测试程序,将执行 fd = open("/dev/my_dev", O_RDONLY);
/mnt # ./app_test
test_4_open: major=10, minor=125
test_4_read
test_4_release
/mnt #
效果图示:
七、篇尾
略……