注:原博文地址http://blog.sina.com.cn/s/blog_4ba5b45e0102v25h.html
-------------------------------------------------------------------------------------------------------------------------------------------
本文將直接了當的帶你進入linux的模塊編譯。當然在介紹的過程當中,我也會添加一些必要的注釋,以便初學者能夠看懂。之所以要寫這篇文章,主要是因為從書本上學的話,可能要花更長的時間才能學會整個過程,因為看書的話是一個學習過程,而我這篇文章更像是一個培訓。所以實踐性和總結性更強。通過本文你將會學到編譯一個模塊和模塊makefile的基本知識。以及加載(卸載)模塊,查看系統消息的一些知識;
聲明:本文為初學者所寫,如果你已經是一個linux模塊編譯高手,還請指正我文章中的錯誤和不足,謝謝
第一步:准備module源代碼
首先我們還是要來編寫一個符合linux格式的模塊文件,這樣我們才能開始我們的模塊編譯。假設我們有一個源文件mymod.c。它的源碼如下:
mymod.c
1 #include <linux/module.h> /* 引入與模塊相關的宏 */ 2 #include <linux/init.h> /* 引入module_init() module_exit()函數 */ 3 #include <linux/moduleparam.h>/* 引入module_param() */ 4 MODULE_AUTHOR("Yu Qiang"); 5 MODULE_LICENSE("GPL"); 6 static int nbr = 10; 7 8 module_param(nbr, int, S_IRUGO); /* 加載模塊時傳入參數 */ 9 10 static int __init yuer_init(void) /* 加載模塊時自動執行 */ 11 { 12 int i; 13 for(i=0; i<nbr; i++) 14 { 15 printk(KERN_ALERT "Hello, How are you. %d\n", i); 16 } 17 return 0; 18 } 19 20 static void __exit yuer_exit(void) 21 { 22 printk(KERN_ALERT"I come from yuer's module, I have been unlad.\n"); 23 } 24 module_init(yuer_init); 25 module_exit(yuer_exit);
我們的源文件就准備的差不多了,這就是一個linux下的模塊的基本結構。
第8行,是導出我們的符號變量nbr,這樣在你加載這個模塊的時候可以動態修改這個變量的值,稍后將演示。
第10行,yuer_init()函數將在模塊加載的時候運行,通過輸出的結果可以看到我們的模塊是否加載成功。
第二步:編寫Makefile文件
首先還是來看看我們Makefile的源文件,然后我們再來解釋;
Makefile
1 obj-m := modules.o #要生成的模塊名 2 modules-objs:= mymod.o #生成這個模塊名所需要的目標文件 3 4 KDIR := /lib/modules/`uname -r`/build 5 PWD := $(shell pwd) 6 7 default: 8 make -C $(KDIR) M=$(PWD) modules 9 10 clean: 11 rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions
現在我來說明一下這個Makefile。請記住是大寫的Makefile而不是小寫的makefile;
obj-m :這個變量是指定你要聲稱哪些模塊,即模塊名稱,模塊的格式為 obj-m := <模塊名>.o,一個目錄下可以有多個obj-m模塊文件,詳見kernel源碼
modules-objs :這個變量是說明聲稱模塊modules需要的目標文件,格式要求 <模塊名>-objs := <目標文件>
切記:模塊的名字不能取與目標文件相同的名字。如在這里模塊名不能取成 mymod;
KDIR :這是我們正在運行的操作系統內核編譯目錄,也就是編譯模塊需要的環境,注意,這兒如果是PC module則路徑為PC內核路徑,如果ARM板則路徑為SDK 內核路徑
M= :指定我們源文件的位置
PWD :這是當前工作路徑$(shell pwd)是make的一個內置函數。用來執行shell命令。
default:第一個目標為Makefile中默認執行的目標
第三步:編譯模塊
現在我們已經准備好了我們所需要的源文件和相應的Makefile。我們現在就可以編譯了。
在終端進入源文件目錄輸入make
運行結果:
1 make[1]: Entering directory `/usr/src/linux-headers-2.6.24-24-generic' 2 CC [M] /home/yuqiang/桌面/mymodule/mymodules.o 3 LD [M] /home/yuqiang/桌面/mymodule/modules.o 4 Building modules, stage 2. 5 MODPOST 1 modules 6 CC /home/yuqiang/桌面/mymodule/modules.mod.o 7 LD [M] /home/yuqiang/桌面/mymodule/modules.ko 8 make[1]: Leaving directory `/usr/src/linux-headers-2.6.24-24-generic'
第四步:加載/卸載我們的模塊
從上面的編譯中我可以看到。已經有一個modules.ko生成了。這就是我們的模塊了。現在我們就可以來加載了。
首先在終端輸入:sudo insmod modules.ko
現在我們來看看我們的模塊加載成功沒有呢?
在終端輸入:dmesg | tail -12 這是查看內核輸出信息的意思。tail -12 顯示最后12條;
顯示結果如下:
1 [17945.024417] sd 9:0:0:0: Attached scsi generic sg2 type 0 2 [18046.790019] usb 5-8: USB disconnect, address 9 3 [19934.224812] Hello, How are you. 0 4 [19934.224817] Hello, How are you. 1 5 [19934.224818] Hello, How are you. 2 6 [19934.224820] Hello, How are you. 3 7 [19934.224821] Hello, How are you. 4 8 [19934.224822] Hello, How are you. 5 9 [19934.224824] Hello, How are you. 6 10 [19934.224825] Hello, How are you. 7 11 [19934.224826] Hello, How are you. 8 12 [19934.224828] Hello, How are you. 9
看到了吧。我們的模塊的初始化函數yuer_init();已經成功運行了。說明我們的模塊已經加載成功;
現在我們再來卸載模塊試試看。
在終端輸入:sudo rmmod modules
在終端輸入:dmesg | tail -3
1 [19934.224826] Hello, How are you. 8 2 [19934.224828] Hello, How are you. 9 3 [20412.046932] I come from yuer's module, I have been unlad.
可以從打印的信息中看到,我們的模塊的退出函數已經被執行了。說明我們的模塊已經被成功的卸載了。到目前位置我們就已經算是對模塊的編譯到編譯運行算是有了一個整體上的認識了。對於以后深入的學習還是應該有點幫助的。下面我們將在看看於模塊相關的一些簡單的操作。
第五步:加載模塊時傳遞參數
在終端輸入:sudo insmod module_name.ko nbr=4
在終端輸入:dmesg | tail -6
顯示結果如下:
1 [20800.655694] Hello, How are you. 9 2 [21318.675593] I come from onefile module, I have been unlad. 3 [21334.425373] Hello, How are you. 0 4 [21334.425378] Hello, How are you. 1 5 [21334.425380] Hello, How are you. 2 6 [21334.425381] Hello, How are you. 3
這樣我們就可以看到在模塊加載的時候動態設置了我們的一個變量。初始化函數中的循環只執行了4次。
可能你會問我怎么知道一個模塊可以設置那些變量呢。當然,你可以先不設變量加載一次。然后可以在終端輸入ls /sys/module/<modules_name>/parameters/來查看。在這里我們是這樣輸入的
在終端輸入:ls /sys/moedle/modules/parameters/
顯示結果:
1 nbr
如果我們的模塊加載成功了。最后我們還可以通過modinfo來查看我們的模塊信息。如下
在終端輸入:sudo modinfo modules.ko
顯示結果:
1 filename: modules.ko 2 license: GPL 3 author: Yu Qiang 4 srcversion: 20E9C3C4E02D130E6E92533 5 depends: 6 vermagic: 2.6.24-24-generic SMP mod_unload 586 7 parm: nbr:int
本文總結:
本文的相關知識都好像有一點淺嘗轍止的感覺。因為本篇文章主要是通過一條線式方式來講解了模塊編寫的相關過程,其實在這個過程中還有很多可以發散的地方。例如:
在寫到MODULE_AUTHOR("Yu Qiang")的時候,你應該想到還有
MODULE_DESCRIPTION(模塊用途的簡單描述);
MODULE_VERSION(模塊的版本字符串);
MODULE_ALIAS(模塊的別名);
...
在寫到module_param(nbr, int, S_IRUGO);的時候,你應該想到還有
EXPORT_SYMBOL(name); 可以導出模塊的函數,這也是模塊編寫的最終目的
...
在用到insmod 和 modinfo的時候。你應該想到還有
depmod 分析可加載模塊的依賴性,並生成modules.dep文件和映射文件
modprobe Linux內核添加刪除模塊
...
如果要成為一名專業的linux模塊開發人員,還要走很多的路,就看各位的了,祝大家學習順利。
我並不是什么linux相關的專家,我只是一個普通的linux相關開發人員(在讀),我的目的也很簡單,就是想給大家介紹一個方法,希望大家在學習的時候不要走過多的彎路。由於個人能力所限難免存在錯誤和缺點,所以請不要介意,如有發現請提出,我立即修改。方便大家學習