Linux核心模組驅動載入與dmesg除錯
因為近期用到了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來進行除錯):
&n