歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核模塊編程指南(一)

Linux內核模塊編程指南(一)

日期:2017/3/2 10:47:08   编辑:Linux內核

  當第一個原始的程序員在最開始的窯洞計算機之牆上鑿過第一個程序時,那是一個在羚羊圖案上畫上“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\n");
/* 如果我們返回一個非零值, 那就意味著
* init_module 初始化失敗並且內核模塊
* 不能加載 */
return 0;
}
/* Cleanup - 撤消 init_module 所做的任何事情 */
void cleanup_module()
{
printk("Short is the life of a kernel module\n");
}

  內核模塊的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 <第二個源文件?gt;.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\n");
/* 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\n");
}

  范例 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版中沒有定義它, 我們可以在必要的時候自己定義它。

Copyright © Linux教程網 All Rights Reserved