-設備驅動介紹& Hello, world!模塊
設備驅動程序就是這個進入Linux內核世界的大門。設備驅動程序在Linux內核中扮演着特殊的角色。它是一個獨立的“黑盒子”,使某個特定硬件響應一個定義好的內部編程接口,這些接口完全隱藏了設備的工作細節。用戶的操作通過一組標準化的調用執行,而這些調用獨立於特定的驅動程序。將這些調用映射到作用於實際硬件的設備特有操作上,則是設備驅動程序的任務。
塊設備:一個塊設備驅動程序主要通過傳輸固定大小的數據來訪問設備。塊設備和字符設備的區別僅僅在於內核內部管理數據的方式,也就是內核及驅動程序之間的軟件接口,而這些不同對用戶程序是透明的。在內核中,和字符驅動程序相比,塊驅動程序具有完全不同的接口。
網絡接口:任何網絡事務都經過一個網絡接口形成,即一個能夠和其他主機交換數據的設備。它可以是個硬件設備,但也可能是個純軟件設備。訪問網絡接口的方法仍然是給它們分配一個唯一的名字(比如eth0),但這個名字在文件系統中不存在對應的節點。內核和網絡設備驅動程序間的通信,完全不同於內核和字符以及塊驅動程序之間的通信,內核調用一套和數據包傳輸相關的函數而不是read、write等。
(1)驅動模塊運行在內核空間,運行時不能依賴於任何標準C庫等應用層的庫、模塊,所以在寫驅動時所調用的函數只能是作爲內核一部分的函數,即使用“EXPORT_SYMBOL”導出的函數。
àinsmod使用公共內核符號表來解析模塊中未定義的符號。公共內核符號表中包含了所有的全局內核項(即函數和變量的地址),這是實現模塊化驅動程序所必須的。
àLinux使用模塊層疊技術,我們可以將模塊劃分爲多個層,通過簡化每個層可縮短開發週期。如果一個模塊需要向其他模塊導出符號,則使用下面的宏:
EXPORT_SYMBOL(name); |
符號必須在模塊文件的全局變量部分導出,因爲這兩個宏將被擴展爲一個特殊變量的聲明,而該變量必須是全局的。
(2)驅動模塊和應用程序的一個重要不同是:應用程序退出時可不管資源釋放或者其他的清除工作,但模塊的退出函數必須仔細撤銷初始化函數所作的一切,否則,在系統重新引導之前某些東西就會殘留在系統中。
(3)處理器的多種工作模式(級別)其實就是爲了操作系統的用戶空間和內核空間設計的。在Unix類的操作系統中只用到了兩個級別:最高和最低級別。
(4)要十分注意驅動程序的併發處理。
(5)內核API中具有雙下劃線(_ _)的函數,通常是接口的底層組件,應慎用。
(6)內核代碼不能實現浮點數運算。參考資料:http://blog.chinaunix.net/u/30180/showart.php?id=1421920
利用Linux設備驅動程序的第一個例程:Hello World模塊瞭解內核驅動模塊的結構。
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL"); |
1. 所有模塊代碼中都包含一下兩個頭文件:
#include <linux/init.h> |
MODULE_LICENSE("Dual BSD/GPL"); |
此外還有可選的其他描述性定義:
MODULE_AUTHOR(""); |
上述MODULE_聲明習慣上放在文件最後。
3. 初始化和關閉初始化的實際定義通常如下:
static int _
_init initialization_function(void) |
清除函數的實際定義通常如下:
static int _
_exit cleanup_function(void) |
4. 一個簡單的Makefile文件:
KERNELDIR = /home/tekkaman/working/SBC2440/linux-2.6.22.2 PWD := $(shell pwd) INSTALLDIR = /home/tekkaman/working/rootfs/lib/modules CROSS_COMPILE = arm-9tdmi-linux-gnu- CC = $(CROSS_COMPILE)gcc obj-m := hello.o .PHONY: modules modules_install clean modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: cp hello.ko $(INSTALLDIR) clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions |
obj-m := hello.o
代表了我們要構造的模塊名爲hell.ko,make 會在該目錄下自動找到hell.c文件進行編譯。如果 hello.o是由其他的源文件生成(比如file1.c和file2.c)的,則在下面加上(注意紅色字體的對應關係):
hello-objs := file1.o file2.o ...... |
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-C $(KERNELDIR) 指定了內核源代碼的位置,其中保存有內核的頂層makefile文件。
M=$(PWD) 指定了模塊源代碼的位置
modules目標指向obj-m變量中設定的模塊。
5. 編譯模塊make modules 、 make modules_install 。
[root@Tekkaman-Ninja Helloworld]# make modules |
[Tekkaman2440@SBC2440V4]#cd /lib/modules/ [Tekkaman2440@SBC2440V4]#ls cs89x0.ko hello.ko p80211.ko prism2_usb.ko [Tekkaman2440@SBC2440V4]#insmod hello.ko Hello, Tekkaman Ninja ! [Tekkaman2440@SBC2440V4]#lsmod Module Size Used by Not tainted hello 1376 0 [Tekkaman2440@SBC2440V4]#rmmod hello Goodbye, Tekkaman Ninja ! Love Linux !Love ARM ! Love KeKe ! [Tekkaman2440@SBC2440V4]#lsmod Module Size Used by Not tainted [Tekkaman2440@SBC2440V4]# |
struct something *item1;
release_thing(item1); fail: |
以下是我的實驗程序:
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- static char *whom = "Tekkaman";
- static int howmany = 1;
- static int param_array[] = {0,0,0,0};
- static int param_array_nr;
- module_param(howmany, int, S_IRUGO);
- module_param(whom, charp, S_IRUGO);
- module_param_array(param_array , int , ¶m_array_nr , S_IRUGO);
- static int hello_init(void)
- {
- int i;
- printk(KERN_ALERT "Hello, Linux !\n");
-
- for (i = 0; i < howmany; i++) {
- printk(KERN_ALERT "(%d) Hello, %s\n", i, whom);
- }
-
- for (i = 0; i < param_array_nr; i++) {
- printk(KERN_ALERT "param_array[%d] : %d \n", i, param_array[i]);
- }
-
- return 0;
- }
- static void hello_exit(void)
- {
- printk(KERN_ALERT "Goodbye, Linux !\n");
- }
- module_init(hello_init);
- module_exit(hello_exit);
- EXPORT_SYMBOL(hello_init);
- MODULE_DESCRIPTION("hello_linux test module");
- MODULE_ALIAS("hello_world");
- MODULE_INFO(tekkaman, "ninja");
- MODULE_VERSION("v1.0");
- MODULE_AUTHOR("Tekkaman");
- MODULE_LICENSE("Dual BSD/GPL");
- root@tekkaman:~# ls
- hello_linux.ko
- root@tekkaman:~# insmod hello_linux.ko param_array=9,8,7
- Hello, Linux !
- (0) Hello, Tekkaman
- param_array[0] : 9
- param_array[1] : 8
- param_array[2] : 7
- root@tekkaman:~# rmmod hello_linux.ko
- Goodbye, Linux !
- root@tekkaman:~# insmod hello_linux.ko param_array=9,8
- Hello, Linux !
- (0) Hello, Tekkaman
- param_array[0] : 9
- param_array[1] : 8
- root@tekkaman:~# rmmod hello_linux.ko
- Goodbye, Linux !
- root@tekkaman:~# insmod hello_linux.ko param_array=9,8,7,6
- Hello, Linux !
- (0) Hello, Tekkaman
- param_array[0] : 9
- param_array[1] : 8
- param_array[2] : 7
- param_array[3] : 6
- root@tekkaman:~# rmmod hello_linux.ko
- Goodbye, Linux !
- root@tekkaman:~# insmod hello_linux.ko param_array=9,8,7,6,5
- param_array: can only take 4 arguments
- hello_linux: `9' invalid for parameter `param_array'
- insmod: error inserting 'hello_linux.ko': -1 Invalid parameters
(15)“#include <linux/sched.h>” 最重要的頭文件之一。包含驅動程序使用的大部分內核API的定義,包括睡眠函數以及各種變量聲明。
(16)“#include <linux/version.h>” 包含所構造內核版本信息的頭文件。
在學習過程中找到了幾篇很好的參考文檔:
(1)第一章 模塊(Modules) URL:http://greenlinux.blogcn.com/diary,103232026.shtml
(2)《從 2.4 到 2.6:Linux 內核可裝載模塊機制的改變對設備驅動的影響》
URL:http://www.ibm.com/developerworks/cn/linux/l-module26/
(3)《Linux2.6內核驅動移植參考》
URL:http://blog.chinaunix.net/u1/40912/showart_377391.html
以上就是我對《Linux設備驅動程序(第3版)》的《第二章 構造和運行模塊》 的學習總結。