前言
最近在學習Linux驅動,記錄下自己學習的歷程。
1.驅動的基本框架
Linux驅動的基本框架包含兩部分,“模塊入口、出口的注冊”和“模塊入口、出口函數的實現”,如下方代碼。
1 static int __init shanwuyan_init(void) //驅動入口函數 2 { 3 return 0; 4 } 5 6 static void __exit shanwuyan_exit(void) //驅動出口函數 7 { 8 9 } 10 11 module_init(shanwuyan_init); //注冊入口函數 12 module_exit(shanwuyan_exit); //注冊出口函數
其中,module_init()和module_exit()兩個函數的作用是注冊驅動的入口“shanwuyan_init”和出口“shanwuyan_exit”。加載驅動時會運行入口函數,卸載驅動時會運行出口函數。入口函數的作用是加載驅動時做一些初始化工作,比如注冊設備、申請設備號、生成設備節點等等,其返回值為int類型;出口函數的作用是卸載驅動時做一些善后操作,比如注銷設備、注銷設備號、銷毀類等等。
2.一個基本驅動的編寫
本文的主要目的是讓讀者了解驅動的基本框架,我們先不實現注冊設備、申請設備號、注銷設備等復雜的工作。
為了讓驅動的加載和卸載工作更直觀地為程序員所觀察,我們可以在入口函數和出口函數中添加打印語句,這樣每次加載和卸載驅動的時候,程序員都能在終端觀察到相應的信息,如下方代碼。
1 static int __init shanwuyan_init(void) //驅動入口函數 2 { 3 printk(KERN_EMERG "shanwuyan_init\r\n"); 4 return 0; 5 } 6 7 static void __exit shanwuyan_exit(void) //驅動出口函數 8 { 9 printk(KERN_EMERG "shanwuyan_exit\r\n"); 10 }
“printk”函數是什么?說到打印,有C語言基礎的讀者首先想到的可能就是“printf”函數,但是“printf”只能在應用層面工作,而設備驅動是工作在內核態下的,所以“printf”不能在設備驅動中工作。在內核態下的打印函數是“printk”函數。KERN_EMERG是打印優先級,這里采用了最高優先級。
再加上頭文件以及注冊用的函數,可以得到一個相對完整的代碼。
1 /* 源代碼文件名為:shanwuyan.c */ 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/fs.h> 6 #include <linux/uaccess.h> 7 8 static int __init shanwuyan_init(void) //驅動入口函數 9 { 10 printk(KERN_EMERG "shanwuyan_init\r\n"); 11 return 0; 12 } 13 14 static void __exit shanwuyan_exit(void) //驅動出口函數 15 { 16 printk(KERN_EMERG "shanwuyan_exit\r\n"); 17 } 18 19 module_init(shanwuyan_init); //注冊入口函數 20 module_exit(shanwuyan_exit); //注冊出口函數
該設備驅動實現的功能是:加載驅動時打印字符串“shanwuyan_init”,卸載驅動時打印字符串“shanwuyan_exit”。
3.Makefile文件的編寫
Makefile文件沒什么可說的,代碼如下(記得匹配自己的內核目錄)。
1 #!/bin/bash 2 3 obj-m += shanwuyan.o #此處要和你的驅動源文件同名 4 5 KDIR := /home/topeet/Android/iTop4412_Kernel_3.0 #這里是你的內核目錄 6 7 PWD ?= $(shell pwd) 8 9 all: 10 make -C $(KDIR) M=$(PWD) modules #make操作 11 12 clean: 13 make -C $(KDIR) M=$(PWD) clean #make clean操作
4.應用
編譯,並加載生成的“shanwuyan.ko”文件,加載驅動和卸載驅動的命令如下。
1 insmod shanwuyan.ko #加載驅動 2 rmmod shanwuyan.ko #卸載驅動,如果該命令不起作用,請用下方的命令 3 rmmod shanwuyan #卸載驅動
進入到驅動文件所在的路徑下,並在命令行輸入加載驅動的命令“insmod shanwuyan.ko”,可以看到驅動入口函數打印出來的字符串信息“shanwuyan_init”。
但是終端還打印了兩行警告信息“shanwuyan: module license 'unspecified' taints kernel”和“Disabling lock debugging due to kernel”,這是因為我們沒有在代碼中加入同意開源協議,所以終端打印該信息。需要注意的是,該警告信息只有在系統啟動后第一次加載驅動時才會打印,卸載掉之后,如果不重啟系統,再加載驅動時就不會再打印這兩行警告信息了。
打開源文件,加入GPL開源協議,一個完整的基本驅動框架就完成了,全部代碼如下。
1 /* 源代碼文件名為:shanwuyan.c */ 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/fs.h> 6 #include <linux/uaccess.h> 7 8 static int __init shanwuyan_init(void) //驅動入口函數 9 { 10 printk(KERN_EMERG "shanwuyan_init\r\n"); 11 return 0; 12 } 13 14 static void __exit shanwuyan_exit(void) //驅動出口函數 15 { 16 printk(KERN_EMERG "shanwuyan_exit\r\n"); 17 } 18 19 module_init(shanwuyan_init); //注冊入口函數 20 module_exit(shanwuyan_exit); //注冊出口函數 21 22 MODULE_LICENSE("GPL"); //同意GPL開源協議,就不會打印警告信息了 23 MODULE_AUTHOR("shanwuyan"); //還可以再添加上作者名稱
再次編譯,重啟系統,並加載驅動,這次不會再打印警告信息了,只打印了我們在入口函數中寫的字符串,如下圖。
使用“rmmod shanwuyan”命令卸載驅動,出現錯誤,如下圖。
這是我們需要創建文件夾“/lib/modules”,創建后再次卸載驅動,又出現了錯誤,如下圖。
我們按照錯誤信息,創建文件夾“/lib/modules/3.0.15”(根據內核版本的不同而不同),再次卸載驅動,成功,打印出來我們想要的字符串信息“shanwuyan_exit”。
本文的全部代碼在這里。