因为近期用到了Linux内核的相关知识,下面随笔将给出内核模块的编写记录,供大家参考。
1、运行环境
Ubuntu 版本:20.04
Linux内核版本:5.4.0-42-generic
gcc版本:gcc version 9.3.0
驱动和一般应用程序的执行方式很大不同
2、内核模块模型说明
(1)驱动和一般应用程序的执行方式很大不同
一般应用由main函数开始执行,流程基本由程序自身控制
驱动程序没有main函数,由回调方式驱动运行
(2)回调方式:
先向内核注册函数,然后应用程序触发这些函数的执行
例如:驱动程序在初始化时,向内核注册处理某个设备写操作的函数
当应用程序使用write系统调用写该设备时,内核就会调用注册的上述函数
3、内核模型常见的回调函数举例
(1)DriverInitialize
驱动初始化函数,通过宏静态注册;
$ insmod PrintModule.ko,安装驱动并触发该函数,通常会创建设备对象;
(2)DriverUninitialize
驱动销毁函数,通过宏静态注册;
$ rmmod PrintModule,卸载驱动并触发该函数;
(3)DriverOpen
打开设备函数,动态注册;
应用调用open函数打开设备对象时,会触发该函数;
(4)DriverRead
读设备函数,动态注册;
应用调用read函数读设备时,会触发该函数;
(5)DriverWrite
写设备函数,动态注册;
应用调用write函数写设备时,会触发该函数;
(7)DriverIOControl
设备控制函数,动态注册;
应用调用ioctl函数操作设备时,会触发该函数;
(8)DriverMMap
设备内存映射函数,动态注册;
应用调用mmap函数时,会触发该函数;
下面给出驱动模块编写函数:
4、DriverMain.c
1 #include "DriverMain.h" 2 3 #include "DriverFileOperations.h" 4 5 #include "ToolFunctions.h" 6 7 8 9 MODULE_LICENSE("Dual BSD/GPL"); 10 11 12 13 struct SLDriverParameters gslDriverParameters = {0}; 14 15 16 17 struct file_operations gslNvmDriverFileOperations = 18 19 { 20 21 .owner = THIS_MODULE, 22 23 .open = DriverOpen, 24 25 .release = DriverClose, 26 27 .read = DriverRead, 28 29 .write = DriverWrite, 30 31 .unlocked_ioctl = DriverIOControl, 32 33 .mmap = DriverMMap, 34 35 }; 36 37 38 39 int InitalizeCharDevice(void) 40 41 { 42 43 int result; 44 45 struct device *pdevice; 46 47 48 49 result = alloc_chrdev_region(&(gslDriverParameters.uiDeviceNumber), 0, 1, DEVICE_NAME); 50 51 if(result < 0) 52 53 { 54 55 printk(KERN_ALERT DEVICE_NAME " alloc_chrdev_region error\n"); 56 57 return result; 58 59 } 60 61 62 63 gslDriverParameters.pslDriverClass = class_create(THIS_MODULE, DEVICE_NAME); 64 65 if(IS_ERR(gslDriverParameters.pslDriverClass)) 66 67 { 68 69 printk(KERN_ALERT DEVICE_NAME " class_create error\n"); 70 71 72 73 result = PTR_ERR(gslDriverParameters.pslDriverClass); 74 75 goto CLASS_CREATE_ERROR; 76 77 } 78 79 80 81 cdev_init(&(gslDriverParameters.slCharDevice), &gslNvmDriverFileOperations); 82 83 gslDriverParameters.slCharDevice.owner = THIS_MODULE; 84 85 86 87 result = cdev_add(&(gslDriverParameters.slCharDevice), gslDriverParameters.uiDeviceNumber, 1); 88 89 if(result < 0) 90 91 { 92 93 printk(KERN_ALERT DEVICE_NAME " cdev_add error\n"); 94 95 goto CDEV_ADD_ERROR; 96 97 } 98 99 100 101 pdevice = device_create(gslDriverParameters.pslDriverClass, NULL, gslDriverParameters.uiDeviceNumber, NULL, DEVICE_NAME); 102 103 if(IS_ERR(pdevice)) 104 105 { 106 107 printk(KERN_ALERT DEVICE_NAME " device_create error\n"); 108 109 110 111 result = PTR_ERR(pdevice); 112 113 goto DEVICE_CREATE_ERROR; 114 115 } 116 117 118 119 return 0; 120 121 122 123 DEVICE_CREATE_ERROR: 124 125 cdev_del(&(gslDriverParameters.slCharDevice)); 126 127 128 129 CDEV_ADD_ERROR: 130 131 class_destroy(gslDriverParameters.pslDriverClass); 132 133 134 135 CLASS_CREATE_ERROR: 136 137 unregister_chrdev_region(gslDriverParameters.uiDeviceNumber, 1); 138 139 140 141 return result; 142 143 } 144 145 146 147 void UninitialCharDevice(void) 148 149 { 150 151 device_destroy(gslDriverParameters.pslDriverClass, gslDriverParameters.uiDeviceNumber); 152 153 154 155 cdev_del(&(gslDriverParameters.slCharDevice)); 156 157 158 159 class_destroy(gslDriverParameters.pslDriverClass); 160 161 162 163 unregister_chrdev_region(gslDriverParameters.uiDeviceNumber, 1); 164 165 } 166 167 168 169 static int DriverInitialize(void) 170 171 { 172 173 DEBUG_PRINT(DEVICE_NAME " Initialize\n"); 174 175 176 177 return InitalizeCharDevice(); 178 179 } 180 181 182 183 static void DriverUninitialize(void) 184 185 { 186 187 DEBUG_PRINT(DEVICE_NAME " Uninitialize\n"); 188 189 190 191 UninitialCharDevice(); 192 193 } 194 195 196 197 module_init(DriverInitialize); 198 199 module_exit(DriverUninitialize);
5、DriverMain.h
1 #ifndef DriverMain_H 2 3 #define DriverMain_H 4 5 6 7 #include <linux/init.h> 8 9 #include <linux/module.h> 10 11 #include <asm/mtrr.h> 12 13 #include <linux/device.h> 14 15 #include <linux/mm.h> 16 17 #include <linux/cdev.h> 18 19 #include <linux/slab.h> 20 21 22 23 #define DEVICE_NAME "msg_printer" 24 25 26 27 struct SLDriverParameters 28 29 { 30 31 struct class *pslDriverClass; 32 33 dev_t uiDeviceNumber; 34 35 struct cdev slCharDevice; 36 37 }; 38 39 40 41 extern struct SLDriverParameters gslDriverParameters; 42 43 44 45 #endif
6、DriverFileOperations.c
1 #include "DriverMain.h" 2 3 #include "DriverFileOperations.h" 4 5 #include "ToolFunctions.h" 6 7 8 9 MODULE_LICENSE("Dual BSD/GPL"); 10 11 12 13 struct SLDriverParameters gslDriverParameters = {0}; 14 15 16 17 struct file_operations gslNvmDriverFileOperations = 18 19 { 20 21 .owner = THIS_MODULE, 22 23 .open = DriverOpen, 24 25 .release = DriverClose, 26 27 .read = DriverRead, 28 29 .write = DriverWrite, 30 31 .unlocked_ioctl = DriverIOControl, 32 33 .mmap = DriverMMap, 34 35 }; 36 37 38 39 int InitalizeCharDevice(void) 40 41 { 42 43 int result; 44 45 struct device *pdevice; 46 47 48 49 result = alloc_chrdev_region(&(gslDriverParameters.uiDeviceNumber), 0, 1, DEVICE_NAME); 50 51 if(result < 0) 52 53 { 54 55 printk(KERN_ALERT DEVICE_NAME " alloc_chrdev_region error\n"); 56 57 return result; 58 59 } 60 61 62 63 gslDriverParameters.pslDriverClass = class_create(THIS_MODULE, DEVICE_NAME); 64 65 if(IS_ERR(gslDriverParameters.pslDriverClass)) 66 67 { 68 69 printk(KERN_ALERT DEVICE_NAME " class_create error\n"); 70 71 72 73 result = PTR_ERR(gslDriverParameters.pslDriverClass); 74 75 goto CLASS_CREATE_ERROR; 76 77 } 78 79 80 81 cdev_init(&(gslDriverParameters.slCharDevice), &gslNvmDriverFileOperations); 82 83 gslDriverParameters.slCharDevice.owner = THIS_MODULE; 84 85 86 87 result = cdev_add(&(gslDriverParameters.slCharDevice), gslDriverParameters.uiDeviceNumber, 1); 88 89 if(result < 0) 90 91 { 92 93 printk(KERN_ALERT DEVICE_NAME " cdev_add error\n"); 94 95 goto CDEV_ADD_ERROR; 96 97 } 98 99 100 101 pdevice = device_create(gslDriverParameters.pslDriverClass, NULL, gslDriverParameters.uiDeviceNumber, NULL, DEVICE_NAME); 102 103 if(IS_ERR(pdevice)) 104 105 { 106 107 printk(KERN_ALERT DEVICE_NAME " device_create error\n"); 108 109 110 111 result = PTR_ERR(pdevice); 112 113 goto DEVICE_CREATE_ERROR; 114 115 } 116 117 118 119 return 0; 120 121 122 123 DEVICE_CREATE_ERROR: 124 125 cdev_del(&(gslDriverParameters.slCharDevice)); 126 127 128 129 CDEV_ADD_ERROR: 130 131 class_destroy(gslDriverParameters.pslDriverClass); 132 133 134 135 CLASS_CREATE_ERROR: 136 137 unregister_chrdev_region(gslDriverParameters.uiDeviceNumber, 1); 138 139 140 141 return result; 142 143 } 144 145 146 147 void UninitialCharDevice(void) 148 149 { 150 151 device_destroy(gslDriverParameters.pslDriverClass, gslDriverParameters.uiDeviceNumber); 152 153 154 155 cdev_del(&(gslDriverParameters.slCharDevice)); 156 157 158 159 class_destroy(gslDriverParameters.pslDriverClass); 160 161 162 163 unregister_chrdev_region(gslDriverParameters.uiDeviceNumber, 1); 164 165 } 166 167 168 169 static int DriverInitialize(void) 170 171 { 172 173 DEBUG_PRINT(DEVICE_NAME " Initialize\n"); 174 175 176 177 return InitalizeCharDevice(); 178 179 } 180 181 182 183 static void DriverUninitialize(void) 184 185 { 186 187 DEBUG_PRINT(DEVICE_NAME " Uninitialize\n"); 188 189 190 191 UninitialCharDevice(); 192 193 } 194 195 196 197 module_init(DriverInitialize); 198 199 module_exit(DriverUninitialize);
7、DriverFileOperations.h
1 #ifndef DriverFileOperations_H 2 3 #define DriverFileOperations_H 4 5 int DriverOpen(struct inode *pslINode, struct file *pslFileStruct) 6 7 int DriverClose(struct inode *pslINode, struct file *pslFileStruct); 8 9 ssize_t DriverRead(struct file *pslFileStruct, char __user *pBuffer, size_t nCount, loff_t *pOffset); 10 11 ssize_t DriverWrite(struct file *pslFileStruct, const char __user *pBuffer, size_t nCount, loff_t *pOffset); 12 13 long DriverIOControl(struct file *pslFileStruct, unsigned int uiCmd, unsigned long ulArg); 14 15 int DriverMMap(struct file *pslFileStruct, struct vm_area_struct *pslVirtualMemoryArea); 16 17 #endif
8、编译内核模块的MakeFile文件:
1 ifneq ($(KERNELRELEASE),) 2 3 obj-m := PrintModule.o 4 5 PrintModule-objs := DriverMain.o DriverFileOperations.o 6 7 EXTRA_CFLAGS := -DTEST_DEBUG -ggdb -O0 8 9 else 10 11 KERNELDIR ?= /lib/modules/$(shell uname -r)/build 12 13 PWD := $(shell pwd) 14 15 default: 16 17 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 18 19 rm *.order *.symvers *.mod.c *.o .*.o.cmd .*.cmd .tmp_versions -rf 20 21 endif
9、运行测试
首先运行MakeFile文件,通过终端输入make命令即可,生成PrintModule.ko和PrintModule.mod:
之后加载内核驱动,通过输入$ sudo insmod PrintModule.ko命令,之后查看dmesg查看驱动信息(也可通过输出调试信息的函数printk来进行调试),具体如下:
随之通过输入$ sudo rmmod PrintModule命令来卸载驱动,也是通过dmesg来查看内核驱动信息(也可通过输出调试信息的函数printk来进行调试):