一,環境搭建步驟
1)下載Linux源碼
1> ~#apt-cache search linux-source
出現:linux-source - Linux kernel source with Ubuntu patches
linux-source-3.0.0 - Linux kernel source for version 3.0.0 with Ubuntu patches
2>~#apt-get install linux-source-3.0.0
下載完成後,在/usr/src/下會出現一個linux-source-3.0.0.tar.bz2。解壓: tar jxvf linux-source-3.0.0.tar.bz2
3>然後在Linux核心源碼目錄/usr/src/linux-source-2.6.32目錄下面用老的方法配置好Linux核心:
~#make oldconfig
4>編譯核心:~#make //大概需要一個小時
5>編譯模組:~#make modules
6>安裝模組:~#make modules_install
以上步驟完成後,會在/lib/modules 目錄下產生一個檔案夾3.0.0-12-generic
二,hello.c
#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){ printk(KERN_ALERT "Hello, world\n"); //printk 跟printf 類似,只是printk運行在核心態 return 0;}static void hello_exit(void){ printk(KERN_ALERT "Goodbye, cruel world\n");}module_init(hello_init);module_exit(hello_exit);
三,Makefile
ifneq ($(KERNELRELEASE),) obj-m :=hello.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif
四,~#make //組建檔案如下
hello.c hello.ko hello.mod.o Makefile modules.order
hello.c~ hello.mod.c hello.o Makefile~ Module.symvers
1>裝載目標模組:~#insmod ./hello.ko
~#lsmod //查看目前安裝的驅動模組,有hello
2>模組裝載觸發hello.c的init()方法,輸出hello world,如果沒有的話,是因為其將輸出放到/var/log/syslog中去了。開啟便可以看見你的結果!
卸載目標模組命令是:~#rmmod ./hello.ko
總結:從此我們邁出了Linux驅動開發的第一步
代碼深度解析:
1)尋找檔案位置:
#include <linux/init.h>//這個標頭檔包含了你的模組初始化與清除的函數#include <linux/module.h>//包含了許多符號與函數的定義,這些符號與函數多與載入模組有關
find / -name module.h
我的檔案位置在:/usr/src/linux-source-3.0.0/include/linux/module.h //其餘的位置也有好多,但是這個檔案位置才是正解
/usr/src/linux-source-3.0.0/include/linux/in.h
2)另外,如果你的模組需要用到參數傳遞,那麼你可能就要聲明moduleparam.h這個標頭檔了。
3)模組裡常包含一些描述性聲明,如:
MODULE_LICENSE("GPL"); // "GPL" 是指明了 這是GNU General Public License的任意版本
// “GPL v2” 是指明 這僅聲明為GPL的第二版本
// "GPL and addtional"
// "Dual BSD/GPL"
// "Dual MPL/GPL"
// "Proprietary" 私人的
// 除非你的模組顯式地聲明一個開源版本,否則核心會預設你這是一個私人的模組(Proprietary)。
MODULE_AUTHOR // 聲明作者
MODULE_DESCRIPTION // 對這個模組作一個簡單的描述,這個描述是"human-readable"的
MODULE_VERSION // 這個模組的版本
MODULE_ALIAS // 這個模組的別名
MODULE_DEVICE_TABLE // 告訴使用者空間這個模組支援什麼樣的裝置
MODULE_聲明可以寫在模組的任何地方(但必須在函數外面),但是慣例是寫在模組最後。
4)
Linux KERN_ALERT 什麼意思
訊息列印層級:
fmt----訊息層級:
#define KERN_EMERG "<0>" #define KERN_ALERT "<1>" #define KERN_CRIT "<2>" #define KERN_ERR "<3>" #define KERN_WARNING "<4>" #define KERN_NOTICE "<5>" #define KERN_INFO "<6>" #define KERN_DEBUG "<7>" |
不同層級使用不同字串表示,數字越小,層級越高。
printk輸出跟輸出的記錄層級有關係,當輸出記錄層級比控制台的層級高時,就會顯示在控制台上,當比控制台低時,則會記錄在/var/log/message中.但是當系統同時運行了klogd和syslogd時,都追加到/var/log/message.在/proc/sys/kernel/printk檔案中,前兩個整數為當前的記錄層級和預設的記錄層級(預設的記錄層級即為printk的輸出層級).
注意:
需要開啟klogd和syslogd服務才能正常輸出。通過klogd可以改變系統訊息輸出層級。
linux0.11為什麼在核心態使用printk()函數,而在使用者態使用printf()函數?
(1) 答:printk()函數是直接使用了向終端寫函數tty_write()。而printf()函數是調用write()系統調用函數向標準輸出裝置寫。所以在使用者態(如進程0)不能夠直接使用printk()函數,而在核心態由於他已是特權級,所以無需系統調用來改變特權級,因而能夠直接使用printk()函數。
printk是核心輸出,在終端是看不見的。
你可以看一下系統日誌。/var/log/message。
或者使用dmesg命令看一下
不管你可能怎麼想,printk()並不是設計用來同使用者互動的,雖然我們在 hello-1就是出於這樣的目的使用它!它實際上是為核心提供日誌功能, 記錄核心資訊或用來給出警告。因此,每個printk() 聲明都會帶一個優先順序,就像你看到的<1>和KERN_ALERT 那樣。核心總共定義了八個優先順序的宏, 所以你不必使用晦澀的數字代碼,並且你可以從檔案linux/kernel.h查看這些宏和它們的意義。如果你 不指明優先順序,預設的優先順序DEFAULT_MESSAGE_LOGLEVEL將被採用。
閱讀一下這些優先順序的宏。標頭檔同時也描述了每個優先順序的意義。在實際中, 使用宏而不要使用數字,就像<4>。總是使用宏,就像 KERN_WARNING。
當優先順序低於int console_loglevel,資訊將直接列印在你的終端上。如果同時 syslogd和klogd都在運行,資訊也同時添加在檔案 /var/log/messages,而不管是否顯示在控制台上與否。我們使用像 KERN_ALERT這樣的高優先順序,來確保printk()將資訊輸出到 控制台而不是只是添加到記錄檔中。 當你編寫真正的實用的模組時,你應該針對可能遇到的情況使用合 適的優先順序。