module 翻譯成中文就是模塊,不過,事實上去翻譯這個字一點都沒意義。在講模塊之前,我先舉一個例子。相信很多人都用過 RedHat。在 RedHat 裡,我們可以執行 sndconfig,它可以幫我們 config 聲卡。config 完之後如果捉得到你的聲卡,那你的聲卡馬上就可以動了,而且還不用重新激活計算機。這是怎幺做的呢 ? 就是靠module。module 其實是一般的程序。但是它可以被動態載到 kernel 裡成為 kernel的一部分。載到 kernel 裡的 module 它具有跟 kernel 一樣的權力。可以 access 任何 kernel 的 data structure。你聽過 kdebug 嗎 ? 它是用來 debug kernel 的。它就是先將它本身的一個 module 載到 kernel 裡,而在 user space 的 gdb 就可以經由跟這個 module 溝通,得知 kernel 裡的 data structure 的值,除此之外,還可以經由載到 kernel 的 module 去更改 kernel 裡 data structure。
我們知道,在寫 C 程序的時候,一個程序只能有一個 main。Kernel 本身其實也是一個程序,它本身也有個 main,叫 start_kernel()。當我們把一個 module 載到 kernel 裡的時候,它會跟 kernel 整合在一起,成為 kernel 的一部分。請各位想想,那 module 可以有 main 嗎 ? 答案很明顯的,是 No。理由很簡單。一個程序只能有一個 main。在使用 module 時,有一點要記住的是 module 是處於被動的角色。它是提供某些功能讓別人去使用的。
Kernel 裡有一個變量叫 module_list,每當 user 將一個 module 載到 kernel 裡的時候,這個 module 就會被記錄在 module_list 裡面。當 kernel 要使用到這個 module 提供的 function 時,它就會去 search 這個 list,找到 module,然後再使用其提供的 function 或 variable。每一個 module 都可以 export 一些 function 或變量來讓別人使用。除此之外,module 也可以使用已經載到 kernel 裡的 module 提供的 function。這種情形叫做 module stack。比方說,module A 用到 module B 的東西,那在加載 module A 之前必須要先加載 module B。否則 module A 會無法加載。除了 module 會 export 東西之外,kernel 本身也會 export 一些 function 或 variable。同樣的,module 也可以使用 kernel 所 export 出來的東西。由於大家平時都是撰寫 user space 的程序,所以,當突然去寫 module 的時候,會把平時寫程序用的 function 拿到 module 裡使用。像是 printf 之類的東西。我要告訴各位的是,module 所使用的 function 或 variable,要嘛就是自己寫在 module 裡,要嘛就是別的 module 提供的,再不就是 kernel 所提供的。你不能使用一般 libc 或 glibc所提供的 function。像 printf 之類的東西。這一點可能是各位要多小心的地方。(也許你可以先 link 好,再載到 kernel,我好象試過,但是忘了)
如果各位的系統有將 set version 的選項打開的話,那大家可以到 /usr/src/linux/include/linux/modules 這個目錄底下。這個目錄底下有所多的 ..ver檔案。這些檔案其實就是用來做 #define 用的。我們來看看 ksyms.ver 這個檔案裡,裡面有一行是這樣子的 :
#define printk _set_ver(printk)
set_ver 是一個 macro,就是用來在 printk 後面加上 version number 的。有興趣的朋友可以自行去觀看這個 macro 的寫法。用了這些 ver 檔,我們就可以在 module 裡直接使用 printk 這樣的名字了。而這些 ver 檔會自動幫我們做好 #define 的動作。可是,我們可以發現這個目錄有很多很多的 ver 檔。有時候,我們怎幺知道我們要呼叫的 function 是在那個 ver 檔裡有定義呢 ? Linux 又幫我們做了一件事。/usr/src/linux/include/linux/modversions.h 這個檔案已經將全部的 ver 檔都加進來了。所以在我們的 module 裡只要 include 這個檔,那名字的問題都解決了。但是,在此,我們奉勸各位一件事,不要將 modversions.h 這個檔在 module 裡 include 進來,如果真的要,那也要加上以下數行:
#ifdef MODVERSIONS
#include
#endif
加入這三行的原因是,避免這個 module 在沒有設定 kernel version 的系統上,將 modversions.h 這個檔案 include 進來。各位可以去試試看,當你把 set version 的選項關掉時,modversions.h 和 modules 這個目錄都會不見。如果沒有上面三行,那 compile 就不會過關。所以一般來講,modversions.h 我們會選擇在 compile 時傳給 gcc 使用。就像下面這個樣子。
gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c \
-include usr/src/linux/include/linux/modversions.h
在這個 command line 裡,我們看到了 -D__KERNEL__,這是說要定義 __KERNEL__ 這個 constant。很多跟 kernel 有關的 header file,都必須要定義這個 constant 才能 include 的。所以建議你最好將它定義起來。另外還有一個 -DMODVERSIONS。這個 constant 我剛才忘了講。剛才我們說要解決 fucntion 或 variable 名字 encode 的方式就是要 include modversions.h,其實除此之外,你還必須定義 MODVERSIONS 這個 constant。再來就是 MODULE 這個 constant。其實,只要是你要寫 module 就一定要定義這個變量。而且你還要 include module.h 這個檔案,因為 _set_ver 就是定義在這裡的。
講到這裡,相信各位應該對 module 有一些認識了,以後遇到 module unresolved 應該不會感到困惑了,應該也有辦法解決了。