Linux模組編程

來源:互聯網
上載者:User

轉自:http://linux.chinaunix.net/techdoc/develop/2008/03/07/982180.shtml


摘要
Linux核心模組編程的資料有些紛繁複雜,有的過於簡單,有的過於龐雜,我試圖用筆記的形式想讀者展示怎樣來進程Linux模組編程,力圖做到簡
明扼要,這篇文章也是作為本人備忘的資料,所以有些地方過於簡略是難免的。本來這篇文章的目的就是讓使用者知其然,至於所以然還是請參考相應的資料,其實最
好的資料莫過於Linux Kernel Source。
適用範圍:

  • Linux Kernel >= 2.6.0


Linux模組簡介
首先這個module不同於microkernel的module,microkernel的module是一個個的daemon進程,工作於使用者
空間,Linux的module只是一個核心的目標代碼,核心通過執行運行時的串連,來把它整合到kernel中去,所以說Linux的module機制
並沒有改變Linux核心為monolithic OS本質,其module也是工作於核心模式,享有核心的所有特權。
至於為什麼要引入Linux Kernle Module(一下簡稱LKM),我想至少有一下幾點:

  • 模組化編程的需要,降低開發和維護成本。
  • 增強系統的靈活性,使得修改一些核心功能而不必重新編譯核心和重啟系統。
  • 降低核心編程的複雜性,使入門門檻降低。


相關宏及標頭檔
LKM需要包含以下標頭檔:  
需要定義以下宏:__KERNEL__, MODULE
一個簡單的核心模組樣本/*file:   hello.c*/
#ifndef __KERNEL__
       #define __KERNEL__
#endif
#ifndef MODULE
       #define MODULE
#endif
#include
#include
static int __init hello_init(void)
{
       printk(KERN_ALERT "Hello, my LKM./n");
       return 0;
}
static void __exit hello_exit(void)
{
       printk(KERN_ALERT "Bye, my LKM./n");
}
module_init(hello_init);
module_exit(hello_exit);
很簡答吧,不是嗎?這個LKM的功能其實也很簡單,就是當通過insmod載入它的時候,他列印Hello, my
LKM.通過rmmod卸載它的時候他列印bye, my
LKM.一個最基本的核心模組一般都包含有兩個函數,一個是初始化函數(比如說這裡的hello_init),一個是卸載函數(hello_exit),
當然也可以沒有任何函數,只是提供一些變數。但是初始化函數和卸載函數必須成對出現。並且init函數當操作成功時傳回值大於等於零,當操作失敗時,返回
非零。宏module_init和module_exit用於註冊初始化函數和卸載函數。
LKM的編譯
一個樣本的Makefile如下所示
obj-m := hello.o
KERNELBUILD := /lib/modules/`uname -r`/build
default:
        make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
        rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions
如果這個目錄下面還有其它模組,只需要在hello.o後面添加就行了。
obj-m := hello.o mod.o
在模組所在目錄執行make,等成功後就可以得到我們想要的模組(hello.ko)了。
如果一個模組存在許多源檔案,比如:hello, 由hello1.c hello2.c共同串連而成,需要在Makefile中加入如下行
hello-objs := hello1.o hello2.o
LKM的載入
Linux為使用者提供了modutils,用來操縱模組。這個工具集主要包括:
insmod 安裝模組
rmmod 刪除模組
modprobe 比較進階的載入和刪除模組,可以解決模組之間的依賴性
lsmod 列出已經載入的模組和其資訊
modinfo 用於查詢模組的相關資訊,比如作者,著作權...
試著用命令insmod hello.ko載入模組,rmmod刪除模組,看看有什麼事情發生了。你有可能看不見任何輸出,難道是有錯誤發生?No,執行命令tail /var/log/message呵呵,是不是看到了?
Feb 19 00:07:35 gentux Hello, my LKM.
Feb 19 00:07:38 gentux Bye, my LKM.
模組其它資訊
比較常用資訊常常包括:作者、描述、著作權等,為此LKM為我們提供了如下宏:
MODULE_AUTHOR("author");
MODULE_DESCRIPTION("the description");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("dev"); // 裝置驅動程式所支援的裝置。
比較常用的Free license有"GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MPL/GPL"。
模組參數
使用者空間的應用程式可以接受使用者的參數,LKM也可以做到,只是方式有些不同而已。相關的宏有:
MODULE_PARM(var, type);
MODULE_PARM_DESC(var, "the description of the var");
模組參數的類型(即MODULE_PARM中的type)有一下幾種:

  • b byte(unsigned char)
  • h short
  • i int
  • l long
  • s string(char*)


這些參數最好有預設值,如果有些必要參數使用者沒有設定可以通過在module_init指定的init函數返回負值來拒絕模組的載入。
LKM還支援數群組類型的模組,如果在類型符號前加上數字n則表示最大程度為n的數組,用“-”隔開的數字分別代表最小和最大的數組長度。
樣本:
MODULE_PARM(var, "4i"); // 最大長度為4的整形數組
MODULE_PARM(var, "2-6i"); // 最小長度為2,最大長度為6的整形數組
如何用insmod傳入參數,其實man一下就可以了,不過現在的man有些過於簡單,所以在此說明一下:
insmod variable=value[,value2...] ...
其中value可以用引號括起來,也可以不用。但是有一點“=”前後不能留有空格,並且value中也不能有空格。
模組符號的匯出
和使用者空間的應用程式不同的是,引入一個模組的目的常常是為了給核心提供一些routine,來完成特定的功能,很少有模組什麼符號都不匯出,為此Linux為使用者提供了如下宏:
EXPORT_SYMBOL(var); // 輸出symbol var
EXPORT_SYMBOL_GPL(var); // 輸出的symbol著作權為GPL
模組之間的依賴性
有的時候兩個模組之間可能有依賴性,要載入的模組A,依賴於模組B,此時insmod是無能為力的,只能用modprobe來載入模組和其依賴的模組,否則只能手動一個個載入。
modprobe通過讀取由depmod -a產生的/lib/modules/version/modules.dep來獲得其所依賴的模組列表(也有可能是一個模組樹),然後調用insmod來一個個按順序載入。
命名空間的問題

  • 對於不需要export的全域symbol最好用static進行修飾,限制其範圍為本檔案,以防汙染核心的命名空間。
  • 對於由核心或其它模組export的一些symbol,最好用extern進行修飾,以示其不在本檔案。
  • 在可能用到errno變數的場合,因為核心沒有export此symbol,只能有使用者自行定義,比如:int errno;
    一個較複雜的模組樣本/*file:   hello.c*/
    #ifndef __KERNEL__
    #define __KERNEL__
    #endif
    #ifndef MODULE
    #define MODULE
    #endif
    #include
    #include
    MODULE_AUTHOR("xiaosuo ");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("This module is a example.");
    static int int_var = 0;
    static const char *str_var = "default";
    static int int_array[6];
    MODULE_PARM(int_var, "i");
    MODULE_PARM_DESC(int_var, "A integer variable");
    MODULE_PARM(str_var, "s");
    MODULE_PARM_DESC(str_var, "A string variable");
    MODULE_PARM(int_array, "2-6i");
    MODULE_PARM_DESC(int_array, "A integer array");
    static int __init hello_init(void)
    {
           int i;
           printk(KERN_ALERT "Hello, my LKM./n");
           printk(KERN_ALERT "int_var %d./n", int_var);
           printk(KERN_ALERT "str_var %s./n", str_var);
           for(i = 0; i
    參考資料
    The Linux Kernel Module Programming Guide
      
    Linux 核心編程指南(第三版)
      
    Linux核心分析及編程

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.