Linux核心模組程式設計指南(一)(轉)

worldblog發表於2007-08-10
Linux核心模組程式設計指南(一)(轉)[@more@]

  當第一個原始的程式設計師在最開始的窯洞計算機之牆上鑿過第一個程式時,那是一個在羚羊圖案上畫上“Hello, world”的程式。羅馬人的程式設計書籍上用“Salut, Mundi”程式開始。我不知道打破這個傳統的人身上發生了什麼而且我想不去追究這個比較安全。

  一個核心模組必須至少有兩個功能: init_module 在該模組被插入核心時被呼叫, cleanup_module 僅僅在它被清除前呼叫。 典型的, init_module 要麼在核心裡為什麼東西登記一個指標,要麼用它自己的程式碼代替核心的某個功能 (通常那個程式碼做一些事情然後呼叫原始的功能). cleanup_module 功能被假定撤消init_module 做的任何事情, 因此模組可以被安全地解除安裝。

  範例 hello.c

  /* hello.c

* Copyright (C) 1998 by Ori Pomerantz

*

* "Hello, world" - 核心模組版本.

*/

/* 必要的標頭檔案 */

/* 核心模組標準 */

#include /* 我們在做核心的工作 */

#include /* 明確的,一個模組 */

/* 處理 CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include

#endif

/* 初始化模組 */

int init_module()

{

printk("Hello, world - this is the kernel speaking ");

/* 如果我們返回一個非零值, 那就意味著

* init_module 初始化失敗並且核心模組

* 不能載入 */

return 0;

}

/* Cleanup - 撤消 init_module 所做的任何事情 */

void cleanup_module()

{

printk("Short is the life of a kernel module ");

}

  核心模組的Make檔案

  一個核心模組單獨是不可執行的,但目標檔案在執行時被連線進核心。因此,在編譯時要使用 -c 標記. 而且, 所有的核心模組必須結合特定的定義過的符號進行編譯。

  __KERNEL__ -- 這個符號告訴標頭檔案這些程式碼將在核心模式執行,不要當作使用者程式的一部分。

  MODULE -- 這個符號告訴標頭檔案為核心模組給出適當的定義。

  LINUX -- 從技術上說這不是必要的。然而,如果你曾經想過寫一系列要在不止一個的作業系統 上編譯的核心模組,那麼你將為你在這所做的高興。這將允許你條件編譯平臺獨立性的部分。

  還有其他一些需要或不需要包括的符號,這取決於核心用什麼標記編譯。如果你不能確定核心是如何編譯的,請檢視 /usr/include/linux/config.h

  __SMP__ -- Symmetrical MultiProcessing(對稱多處理). 如果核心被編譯為支援對稱多處理 (即使它僅僅執行於單CPU上),這個符號必須包含.如果你使用對稱多處理,你還有很多其他的事情需要 做(參看第12章).

  CONFIG_MODVERSIONS -- 如果CONFIG_MODVERSIONS 被啟用, 你需要使它在編譯核心模組時已定義 並且包含/usr/include/linux/modversions.h. 這也可以比、被程式碼自己完成。

  範例 Makefile

  # 為基本的核心模組寫的Make檔案

  CC=gcc

MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX

hello.o: hello.c /usr/include/linux/version.h

$(CC) $(MODCFLAGS) -c hello.c

echo insmod hello.o to turn it on

echo rmmod hello to turn if off

echo

echo X and kernel programming do not mix.

echo Do the insmod and rmmod from outside X.

  因此,現在唯一剩下的事情就是su 為root使用者 (你不能作為root使用者編譯,對嗎? 生活在邊緣1.1...), 然後insmod hello 和 rmmod hello 到你的系統核心. 當你完成這個,注意 /proc/modules中的新核心模組。

  順便說一下,之所以推薦不要在 X下做insmod是因為當核心用printk列印訊息時它將訊息傳送到控制檯。當你不使用 X, 它就發往你正在使用的虛擬終端(你用Alt-F選定的那個) 而使你可以看見。另一方面,當你使用 X, 有兩種可能。要麼你有一個用xterm -C開啟的終端,在這種情況下輸出將被髮送到那兒;或者你沒有開啟終端,在這種情況下輸出被髮往被X“隱蔽”的虛擬終端7。

  如果你的核心變得不穩定,不用X得到除錯資訊更有可能。在X之外printk 直接從核心列印到控制檯。另一方面,在X下,printk變成使用者模式程式 (xterm -C). 當該程式正佔用CPU,它被假設發往X服務程式,然後,當X服務程式佔用 CPU, 它被假設去顯示該資訊 --但是一個不穩定的核心通常意味著崩潰或重新啟動然而你不想延遲得到錯誤資訊,這也許可以向你解釋什麼地方出錯了。

  多檔案核心模組

  有時候將核心模組分為幾個原始檔是有意義的,你需要做下面的事情:

  1. 除了一個檔案外在所有的原始檔中加入一行 #define __NO_VERSION__. 這是很重要的,因為module.h通常包含kernel_version的定義, 它是一個和模組一起編譯的核心版本的全域性變數。如果你需要version.h, 你需要自己包含它,因為module.h 在有 __NO_VERSION__的定義的情況下不自動包含它。

  2. 像通常那樣編譯所有的原始檔。

  3. 將所有的目標檔案合併成一個。在x86架構下使用 ld -m elf_i386 -r -o < 模組名>.o .o

  這裡有一個這樣的核心模組的範例。

  範例 start.c

  /* start.c

* Copyright (C) 1999 by Ori Pomerantz

*

* "Hello, world" - 核心模組版本.

* 這個檔案只包含啟動程式

*/

/* 必要的標頭檔案 */

/* 核心模組標準標頭檔案 */

#include /* 我們正在做核心的工作 */

#include /* 明確的指定是核心模組 */

/* 處理 CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include

#endif

/* 初始化模組 */

int init_module()

{

printk("Hello, world - this is the kernel speaking ");

/* If we return a non zero value, it means that

* init_module failed and the kernel module

* can't be loaded */

return 0;

}

  範例 stop.c

  /* stop.c

* Copyright (C) 1999 by Ori Pomerantz

*

* "Hello, world" - 核心模組版本

* 這個檔案只包含終止程式

*/

/* 必要的標頭檔案 */

/* 核心模組的標準標頭檔案 */

#include /* 我們正在做核心的工作 */

#define __NO_VERSION__ /* 這不是核心模組檔案 */

#include /* 明確的指定是核心模組 */

#include /* 因為有 __NO_VERSION__ 而不能被自動包含*/

/* 處理 CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include

#endif

/* Cleanup - 撤消init_module 所做的任何事情 */

void cleanup_module()

{

printk("Short is the life of a kernel module ");

}

  範例 Makefile

  # 多檔案核心模組的Make檔案

  CC=gcc

MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX

hello.o: start.o stop.o

ld -m elf_i386 -r -o hello.o start.o stop.o

start.o: start.c /usr/include/linux/version.h

$(CC) $(MODCFLAGS) -c start.c

stop.o: stop.c /usr/include/linux/version.h

$(CC) $(MODCFLAGS) -c stop.c

  多核心版本原始檔

  核心展現給程式的主要介面是系統呼叫,它通常跨版本保持相同。新的系統呼叫被加入,但通常老的保持和原來嚴格的一樣。這對於向後相容性是必要的--新的核心版本不應打破常規的程式。在大多情況下,裝置檔案也將保持相同。另一方面,和核心內部的介面可以並且在版本之間有改變。

  Linux核心版本分為穩定版 (n..m) 和開發版 (n..m).開發版包含所有的好的新思想,包括那些被認為是錯誤或需要在下一版重新實現的東西。結果,你不能期盼在那些版本中介面保持相同(這也是我為什麼不在這本書中操心去支援它的原因,那需要太多的工作並且很快就過時了)。另一方面,在穩定版中我們可以期盼介面保持相同,除了錯誤修訂版(數字m)。

  這個版本的核心模組程式設計指南包括對 2.0.x 和2.2.x 核心版本的支援。既然這兩個版本間有差異,這就需要根據版本進行條件編譯。可以使用宏 LINUX_VERSION_CODE來做這件事。在 a.b.c 版的核心中,這個宏的值是 216a+28b+c。為了 得到某個核心版本的值,我們可以使用 KERNEL_VERSION 宏. 因為在 2.0.35版中沒有定義它, 我們可以在必要的時候自己定義它。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-940290/,如需轉載,請註明出處,否則將追究法律責任。

相關文章