Linux 2.6.x 核心模組入門(LKM)

來源:互聯網
上載者:User
摘要

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

適用範圍:

  • Linux Kernel >= 2.6.0

#sudo apt-get install linux-kernel-devel

安裝核心標頭檔

#sudo apt-get install linux-headers-`uname -r`

當然, gcc /make 等工具天生就是需要的。

安裝kernel 必須的開發庫


Linux模組簡介

首先這個module不同於microkernel的module,microkernel的module是一個個的daemon進程,工作於使用者 空間,Linux的module只是一個核心的目標代碼,核心通過執行運行時的串連,來把它整合到kernel中去,所以說Linux的module機制 並沒有改變Linux核心為monolithic OS本質,其module也是工作於核心模式,享有核心的所有特權。

至於為什麼要引入Linux Kernle Module(一下簡稱LKM),我想至少有一下幾點:

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


相關宏及標頭檔

LKM需要包含以下標頭檔:<linux/kernel.h> <linux/module.h>

需要定義以下宏:__KERNEL__, MODULE


一個簡單的核心模組樣本

/*file:   hello.c*/

#ifndef __KERNEL__

#define __KERNEL__

#endif

#ifndef MODULE

#define MODULE

#endif

#include <linux/module.h>

#include <linux/kernel.h>

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.

也可以用dmesg 就可以看到 產生的核心message


模組其它資訊

比較常用資訊常常包括:作者、描述、著作權等,為此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來一個個按順序載入。


命名空間的問題

  1. 對於不需要export的全域symbol最好用static進行修飾,限制其範圍為本檔案,以防汙染核心的命名空間。
  2. 對於由核心或其它模組export的一些symbol,最好用extern進行修飾,以示其不在本檔案。
  3. 在可能用到errno變數的場合,因為核心沒有export此symbol,只能有使用者自行定義,比如:int errno;


一個較複雜的模組樣本

/*file:   hello.c*/

#ifndef __KERNEL__

#define __KERNEL__

#endif

#ifndef MODULE

#define MODULE

#endif

#include <linux/module.h>

#include <linux/kernel.h>

MODULE_AUTHOR("xiaosuo <xiaosuo@gmail.com>");

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 < 6; i ++){

printk("int_array[%d] = %d"n", i, int_array[i]);

}

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_ALERT "Bye, my LKM."n");

}

module_init(hello_init);

module_exit(hello_exit);


參考資料

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.