《Linux內核精髓:精通Linux內核必會的75個絕技》一HACK #3 如何編寫內核模塊

HACK #3 如何編寫內核模塊

本節將介紹向Linux內核中動態添加功能的結構—內核模塊的編寫方法。
內核模塊
Linux內核是單內核(monolithic kernel),也就是全部的內核功能都集成在一個內核空間內。可是內核具備模塊功能,能夠將磁盤驅動程序、文件系統等獨立的內核功能製做成模塊,並動態添加到內核空間或者刪除。
內核模塊是能夠動態添加到Linux內核空間的二進制文件,文件擴展名爲ko。
內核模塊的編寫方法大體有兩種。一種是將內核源碼樹帶有的功能編寫爲模塊的方法(參考Hack #2),另外一種是將內核源碼樹中所沒有的特有功能編寫爲模塊的方法。
經過內核配置編寫模塊
把內核源代碼文件中CONFIG_*=m的項目所對應的驅動程序編寫爲模塊。編寫生成的模塊通常安裝在/lib/modules/內核版本/kernel下。
以RHEL6爲例javascript

# ls /lib/modules/2.6.32-71.29.1.el6.x86_64/kernel/ arch crypto drivers fs kernel lib mm net sound 

編寫特有的內核模塊
下面將介紹如何編寫內核源碼樹中所沒有的特有內核模塊。
以mymod模塊爲例說明,請將下面的代碼以mymod.c爲文件名保存。java

#include <linux/module.h> #include <linux/timer.h> #include <linux/errno.h> static int sec = 5; module_param(sec, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(sec, "Set the interval."); static void mymod_timer(unsigned long data); static DEFINE_TIMER(timer, mymod_timer, 0, 0); static void mymod_timer(unsigned long data) { printk(KERN_INFO "mymod: timer\n"); mod_timer(&timer, jiffies + sec * HZ); } static int mymod_init(void) { printk(KERN_INFO "mymod: init\n"); if (sec <= 0) { printk(KERN_INFO "Invalid interval sec=%d\n", sec); return -EINVAL; } mod_timer(&timer, jiffies + sec * HZ); return 0; } static void mymod_exit(void) { del_timer(&timer); printk(KERN_INFO "mymod: exit\n"); } module_init(mymod_init); module_exit(mymod_exit); MODULE_AUTHOR("Hiroshi Shimamoto"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("My module"); 

在模塊的源代碼中包含(include)頭文件linux/module.h。
名爲module_int()和module_exit()的宏,能夠調用回調(callback)函數來進行初始化和終止模塊的處理。在模塊的源文件中進行以下描述,就能夠在添加模塊時調用初始化函數,在刪除模塊時調用終止函數。
module_init(初始化函數名);
module_exit(終止函數名);
在這個例子模塊的情形下調用的分別是mymod_init()和mymod_exit()。
初始化函數爲了表示初始化已正常完成,須要返回0。按照Linux內核中的寫法,發生錯誤(error)時將返回一個值爲負數的錯誤代碼。在這個例子中,若是設定值出錯,則處理爲-EINVAL(非法值)。
下面先用3個宏對模塊進行定義,但在模塊編寫中並非必需的。
imagelinux

這個例子模塊還用到了模塊參數。模塊參數可使用module_param()宏來生成。
module_param(參數名,參數類型,權限(permission));
在例子模塊中,sec定義爲int類型的模塊參數。
另外,還可使用MODULE_PARM_DESC()宏來對模塊參數進行說明。
先簡單介紹一下這個例子的運行過程。當添加模塊時,會調用指定爲初始化函數的mymod_init()。在mymod_init()中首先經過printk()輸出:
mymod: init
而後確認模塊參數sec是否正常。在模塊參數sec的值爲0如下的異常情形時,會返回EINVAL錯誤代碼並終止程序。在判斷模塊參數sec正常後,將內核計時器設置爲sec秒後啓動超時(timeout)函數mymod_timer()。在每隔sec秒啓動的mymod_timer()中,首先使用printk()輸出:框架

mymod: timer

再次設置sec秒的內核計時器,而後終止。當刪除模塊時,會調用mymod_exit()函數,刪除內核計時器,經過printk()輸出:函數

mymod: exit

因而模塊終止。
接下來須要準備編寫模塊所需的Makefile。因爲是使用內核的建立框架來生成,所以Makefile的內容很是簡單。ui

obj-m :=mymod.o

最後執行下列make命令,經過當前目錄(current directory)的源代碼和Makefile生成模塊mymod.ko。spa

# make -C /lib/modules/'uname 杛'/build M='pwd' 

經過使用modinfo命令,能夠看到所生成模塊mymod.ko的信息。從這裏能夠看到使用MODULE_*宏所指定的內容。3d

# modinfo mymod.ko
filename:       mymod.ko
description:    My module license: GPL author: Hiroshi Shimamoto srcversion: 61A3BB7CFC0C89B8344F5A5 depends: vermagic: 2.6.32-71.29.1.el6.x86_64 SMP mod_unload modversions parm: sec:Set the interval. (int) 

添加內核模塊
添加內核模塊須要用到insmod命令或modprobe命令。
經過執行insmod命令把生成的mymod.ko模塊添加進來。code

# insmod mymod.ko

使用dmesg命令,能夠看到例子模塊mymod.ko的輸出內容。blog

# dmesg | tail
       :
mymod: init

做爲模塊初始化函數mymod_init()所調用的printk()的輸出內容會在最後一行顯示。
使用lsmod能夠顯示目前添加到內核中的模塊列表。

# lsmod
Module                Size   Used by
mymod                 1482 0 
:

能夠看到,mymod行存在,模塊已添加。
要將已添加的模塊從內核空間刪除時,可使用rmmod命令。

# rmmod mymod

執行rmmod命令後,模塊將從內核空間內刪除,使用lsmod命令就不會再輸出mymod行。
此外,使用dmesg命令還能夠看到終止模塊的處理中printk()輸出的信息mymod: exit。

# dmesg | tail
       :
mymod: exit

下面針對模塊參數做一些介紹。在添加模塊後,就會在/sys/module下生成對應的目錄和文件。

# ls /sys/module/mymod/ 

holders initstate notes parameters refcnt sections srcversion
能夠確認在parameters下生成的模塊mymod中所定義的參數sec。

# ls -l /sys/module/mymod/parameters/sec 

-rw-r--r--. 1 root root 4096 May 15 06:34 /sys/module/mymod/parameters/sec
其內容應當是初始值5。

# cat /sys/module/mymod/parameters/sec 

5
模塊參數能夠在使用insmod添加模塊時對值進行指定。

# insmod mymod.ko sec=10 

進行上述操做後,添加mymod.ko時模塊參數sec就爲10,默認間隔5秒的超時變成間隔10秒。小結本節介紹了內核模塊的編寫方法。編寫特有內核模塊是Kernel構建的入門級操做,你也能夠嘗試一下。參考文獻Documentation/kbuild/modules.txt—Hiroshi Shimamoto

相關文章
相關標籤/搜索