-- TOC --
将上一篇的驱动稍微做复杂并完善一些,注册4个minor,使用比较完善的Makefile,制作load和unload的shell脚本。
这次测试需要4个文件:
$ tree
.
├── load.sh
├── Makefile
├── mychar.c
└── unload.sh
0 directories, 4 files
mychar驱动源码:
$ cat mychar.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
#define MINOR_FIRST 5
#define MINOR_NUM 4
unsigned int major = 0;
struct cdev mychar[MINOR_NUM];
struct file_operations mychar_fop;
int mychar_open(struct inode *inode, struct file *fp) {
printk(KERN_INFO"[mychar] open called, major %u minor %u.\n",
imajor(inode), iminor(inode));
return 0;
}
int mychar_release(struct inode *inode, struct file *fp) {
printk(KERN_INFO"[mychar] release called, major %u minor %u.\n",
imajor(inode), iminor(inode));
return 0;
}
struct file_operations mychar_fop = {
.owner = THIS_MODULE,
.open = mychar_open,
.release = mychar_release,
};
static void mychar_exit(void) {
int i;
for (i=0; i<MINOR_NUM; ++i) cdev_del(&mychar[i]);
unregister_chrdev_region(MKDEV(major,MINOR_FIRST), MINOR_NUM);
printk(KERN_INFO"[mychar] exit.\n");
}
static int __init mychar_init(void) {
int rn, i;
dev_t dev;
/* get a major number */
if ((rn = alloc_chrdev_region(&dev,MINOR_FIRST,MINOR_NUM,"mychar"))) {
printk(KERN_WARNING"[mychar] can't get major number, err %d.\n", rn);
return rn;
}
major = MAJOR(dev);
printk(KERN_INFO"[mychar] major is %d, %d minor start from %d.\n",
major, MINOR_NUM, MINOR_FIRST);
/* init and add cdev */
memset(mychar, 0, sizeof(struct cdev)*MINOR_NUM);
for (i=0; i<MINOR_NUM; ++i) {
cdev_init(&mychar[i], &mychar_fop);
if ((rn = cdev_add(&mychar[i],MKDEV(major,i+MINOR_FIRST),1))) {
printk(KERN_WARNING"[mychar] cdev_add err %d, minor %d.\n",
rn, i+MINOR_FIRST);
mychar_exit();
return rn;
}
}
return 0;
}
module_init(mychar_init);
module_exit(mychar_exit);
为了测试,刻意将minor设置为从5开始,连续4个。
Makefile如下:
$ cat Makefile
obj-m := mychar.o
PWD := $(shell pwd)
SRC := ~/sources/linux-5.14.14
default:
$(MAKE) -C $(SRC) M=$(PWD) modules
clean:
rm -f *.o *.ko *.cmd *.dwo *.mod *.mod.c Module.symvers modules.order
rm -f .m* .M*
make:编译驱动;
make clean:删除所有过程文件。
由于驱动要管理4个minor,在mknod的时候,手动查找major,手动创建node等操作都比较繁琐,因此全部脚本化,下面是load和unload脚本:
$ cat load.sh
#!/usr/bin/bash
device=mychar
insmod ${device}.ko || exit 1
rm -f ${device}-{5..8}
major=$(awk "\$2==\"${device}\" {print \$1}" /proc/devices)
for i in {5..8}; do
mknod -m 666 ${device}-$i c ${major} $i
done
$ cat unload.sh
#!/usr/bin/bash
device=mychar
rm -f ${device}-{5..8}
rmmod $device
这4个文件都准备好后,开始测试:
$ pwd
/home/xinlin/test/mychar2
$ ls
load.sh Makefile mychar.c unload.sh
$ make
make -C ~/sources/linux-5.14.14 M=/home/xinlin/test/mychar2 modules
make[1]: Entering directory '/home/xinlin/sources/linux-5.14.14'
CC [M] /home/xinlin/test/mychar2/mychar.o
MODPOST /home/xinlin/test/mychar2/Module.symvers
CC [M] /home/xinlin/test/mychar2/mychar.mod.o
LD [M] /home/xinlin/test/mychar2/mychar.ko
make[1]: Leaving directory '/home/xinlin/sources/linux-5.14.14'
$ sudo bash load.sh
$ python3 -c 'open("mychar-5").close()'
$ python3 -c 'open("mychar-7").close()'
$ python3 -c 'open("mychar-6").close()'
$ python3 -c 'open("mychar-8").close()'
$ sudo bash unload.sh
$ make clean
rm -f *.o *.ko *.cmd *.dwo *.mod *.mod.c Module.symvers modules.order
rm -f .m* .M*
$ ls
load.sh Makefile mychar.c unload.sh
$ dmesg | tail -n10
[894999.337701] [mychar] major is 237, 4 minor start from 5.
[895040.259197] [mychar] open called, major 237 minor 5.
[895040.259496] [mychar] release called, major 237 minor 5.
[895043.090805] [mychar] open called, major 237 minor 7.
[895043.091057] [mychar] release called, major 237 minor 7.
[895046.650857] [mychar] open called, major 237 minor 6.
[895046.651108] [mychar] release called, major 237 minor 6.
[895049.562412] [mychar] open called, major 237 minor 8.
[895049.562658] [mychar] release called, major 237 minor 8.
[895066.520915] [mychar] exit.
Nice~~!!下次开始研究read和write接口。
open系统调用会创建file结构体,当kernel要释放file结构体之前,才会驱动的release!fork系统调用,只是增加file结构体内的count,并不会创建新的file结构体。用户程序关闭时,kernel会自动将为close的file进行关闭,用户程序可以比较lazy。当file的count为0的时候,驱动的release被调用。如果在Python中,连续两次open,相当于两次open系统调用,两次创建的file结构体不同。
BUGS:
如果4个device只有部分add成功,exit的时候确还是全部做del,此时会看到这样的info:
[961914.546065] [mychar] major is 237, 4 minor start from 5.
[961918.226288] ------------[ cut here ]------------
[961918.226321] kobject: '(null)' (00000000cab924f3): is not initialized, yet kobject_put() is being called.
[961918.226329] WARNING: CPU: 0 PID: 481630 at lib/kobject.c:750 kobject_put+0x52/0x1a0
[961918.226333] Modules linked in: mychar(OE-) vsock_loopback vmw_vsock_virtio_transport_common vmw_vsock_vmci_transport vsock nls_iso8859_1 snd_ens1371 intel_rapl_msr snd_ac97_codec intel_rapl_common gameport ac97_bus snd_pcm crct10dif_pclmul ghash_clmulni_intel aesni_intel snd_seq_midi crypto_simd snd_seq_midi_event vmw_balloon snd_rawmidi cryptd snd_seq joydev input_leds rapl snd_seq_device snd_timer snd soundcore serio_raw vmw_vmci mac_hid sch_fq_codel vmwgfx ttm drm_kms_helper cec rc_core fb_sys_fops syscopyarea sysfillrect sysimgblt msr parport_pc ppdev lp drm parport ip_tables x_tables autofs4 hid_generic usbhid hid crc32_pclmul psmouse ahci libahci pcnet32 mii mptspi mptscsih mptbase i2c_piix4 scsi_transport_spi pata_acpi [last unloaded: mychar]
[961918.226364] CPU: 0 PID: 481630 Comm: rmmod Tainted: G W OE 5.14.14 #1
[961918.226384] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/22/2020
[961918.226386] RIP: 0010:kobject_put+0x52/0x1a0
[961918.226387] Code: 24 38 83 f8 01 74 28 85 c0 0f 8e 9b 00 00 00 5b 41 5c 41 5d 41 5e 5d c3 48 8b 37 48 89 fa 48 c7 c7 88 d8 7f 91 e8 87 67 5b 00 <0f> 0b eb c3 c3 4d 8b 74 24 18 49 8b 5c 24 28 4d 8b 2c 24 66 90 48
[961918.226389] RSP: 0018:ffffa48642bf3e80 EFLAGS: 00010282
[961918.226390] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000027
[961918.226391] RDX: 0000000000000027 RSI: 00000000ffff7fff RDI: ffff9188bbe189c8
[961918.226392] RBP: ffffa48642bf3ea0 R08: ffff9188bbe189c0 R09: ffffa48642bf3c58
[961918.226393] R10: 0000000000000001 R11: 0000000000000001 R12: ffffffffc05ce5b8
[961918.226394] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
[961918.226395] FS: 00007fead8222540(0000) GS:ffff9188bbe00000(0000) knlGS:0000000000000000
[961918.226412] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[961918.226412] CR2: 00005648a4909658 CR3: 0000000012156006 CR4: 00000000003706f0
[961918.226431] Call Trace:
[961918.226450] cdev_del+0x28/0x30
[961918.226455] mychar_exit+0x39/0x5d [mychar]
[961918.226457] __x64_sys_delete_module+0x14a/0x260
[961918.226460] ? exit_to_user_mode_prepare+0x3c/0x1e0
[961918.226462] do_syscall_64+0x3b/0xc0
[961918.226465] entry_SYSCALL_64_after_hwframe+0x44/0xae
[961918.226468] RIP: 0033:0x7fead836ebcb
[961918.226469] Code: 73 01 c3 48 8b 0d c5 82 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 95 82 0c 00 f7 d8 64 89 01 48
[961918.226470] RSP: 002b:00007fff2f2434c8 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
[961918.226472] RAX: ffffffffffffffda RBX: 00005648a48fe750 RCX: 00007fead836ebcb
[961918.226473] RDX: 000000000000000a RSI: 0000000000000800 RDI: 00005648a48fe7b8
[961918.226473] RBP: 00007fff2f243528 R08: 0000000000000000 R09: 0000000000000000
[961918.226474] R10: 00007fead83eaac0 R11: 0000000000000206 R12: 00007fff2f243700
[961918.226475] R13: 00007fff2f2457a1 R14: 00005648a48fe2a0 R15: 00005648a48fe750
[961918.226476] ---[ end trace 54efefb107b6bdc6 ]---
[961918.226478] [mychar] exit.
驱动模块还是能够退出!
本文链接:https://cs.pynote.net/sf/linux/dd/202112066/
-- EOF --
-- MORE --