因為近期用到了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來進行調試):