一、篇头
使用自行组建的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-stable
obj-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) clean
rm -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.o
MODPOST /home/szhou/works/qemu_linux/linux-stable/my_kmodules/Module.symvers
LD [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 #
效果图示:
七、篇尾
略……