迄今為止,我們有兩中辦法從核心模組中產生輸出:我們可以登記乙個裝置驅動程式並 mknod 乙個裝置檔案,或者我們可以建立乙個/proc檔案。這可以讓核心模組告訴我們任何它可能告訴我們的事情。唯一的問題是這沒有辦法讓我們告訴它。我們將輸入傳送給核心模組的第乙個辦法將是通過寫回 /proc 檔案。
因為 proc 檔案系統主要是為了讓核心報告它對程序的狀態,所以對輸入沒有專門的預備。 proc_dir_entry結構沒有包含乙個指向輸入函式的指標而包含輸出函式的指標。為了向/proc 檔案中寫,我們需要使用標準的檔案系統機制。
在 linux 中對檔案系統登記有標準的機制。既然每個檔案系統必須有它自己的處理節點和檔案操作(兩者的不同在於檔案操作處理檔案自己,而節點操作處理對檔案的引用,例如建立對它的連線)的函式, 所以有乙個特殊的結構儲存所有這些函式的指標, inode_operations 結構, 包含乙個指向 file_operations結構的指標。在 /proc 中,任何時候登記乙個新檔案我們都允許特別指定哪個 inode_operations 結構將用於訪問它。這就是我們使用的機制, inode_operations 結構包含指向 file_operations 結構的指標,而它又包含指向我們的module_input 和 module_output 函式的指標。
注意在核心中標準的讀寫的任務是顛倒的。讀函式用作輸出而寫函式用於輸入。造成這個局面的原因是讀寫是依據使用者的觀點--如果乙個程序從核心中讀什麼,那麼核心就需要輸出它,而如果程序向核心中寫什麼,那麼核心就需要將它作為輸入接收。
這兒另乙個引起注意的地方是 module_permission 函式。這個函式在程序試圖用 /proc檔案做什麼的時候被呼叫,並且它決定是否允許訪問。現在它僅僅基於操作和當前使用者的uid(就像 current 中的那樣,乙個指向包含當前執行程序的資訊的結構的指標),但它也可以基於任何我們喜歡的東西,例如其他程序在用該檔案做什麼,時間,或者我們上次的接收的輸入。
使用 put_user 和 get_user 的原因是在 linux 中記憶體 (在intel 架構下,在其他處理器下可能不同)是分段的。這意味著指標不能單獨由它自己指向乙個唯一的記憶體位置,只是在記憶體的段中的位置,你需要知道它可以使用哪個記憶體段。對核心只有乙個記憶體段,其他程序也各有乙個。
程序只能訪問自己的記憶體段,因此當寫普通的作為程序執行的程式時我們不必為段操心。當你寫核心模組時,通常你想訪問核心的記憶體段,它由系統自動的處理。然而,當記憶體緩衝區中的內容需要在當前程序和核心中傳遞時,核心的函式收到的是指向位於程序的記憶體段中的記憶體緩衝區的指標。 put_user 和 get_user 巨集可以讓你訪問那些記憶體。
範例 procfs.c
/* procfs.c - 建立乙個可以輸入輸出的在 /proc 中的「檔案」。 */
/* 必要標頭檔案 */
/* 標準標頭檔案 */
#include /* 核心工作 */
#include /* 明確指定是模組 */
/* 處理 config_modversions */
#if config_modversions==1
#define modversions
#include
#endif
/* 使用 proc 檔案系統所必要的 */
#include
/* 在 2.2.3 版/usr/include/linux/version.h 包含這個巨集,但是
* 在 2.0.35 版中不包含--因此加入這個以備需要。 */
#ifndef kernel_version
#define kernel_version(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#if linux_version_code >= kernel_version(2,2,0)
#include /* 得到 get_user 和 put_user */
#endif
/* 模組的檔案函式 ********************** */
/* 在這兒儲存上次收到的資訊以證明我們可以處理我們的輸入。 */
#define message_length 80
static char message[message_length];
/* 既然我們使用了檔案操作結構我們就不能使用預備的那個特殊的proc輸出函式
* 我們不得不使用標準的讀函式,就是這個函式。*/
#if linux_version_code >= kernel_version(2,2,0)
static ssize_t module_output(
struct file *file, /* 要讀的檔案 */
char *buf, /* 要將資料放入的緩衝區(在使用者記憶體段中) */
size_t len, /* 緩衝區長度 */
loff_t *offset) /* 檔案偏移量--忽略 */
#else
static int module_output(
struct inode *inode, /* 要讀的節點 */
struct file *file, /* 要讀的檔案 */
char *buf, /* 要將資料放入的緩衝區(在使用者記憶體段中) */
int len) /* 緩衝區長度 */
#endif
/* 我們使用 put_user 將字串從核心的記憶體段中拷貝到呼叫我們的檔案的程序的記憶體段中。
* 順便說一下, get_user的用法相反。*/
sprintf(message, "last input:%s", message);
for(i=0; i put_user(message[i], buf+i);
/* 注意,我們假設訊息的長度小於 len,或者被截短。在真實的情況下,如果訊息的長度小於
* len 那麼我們會返回 len 而在下次呼叫時將用訊息的第 len+1 個位元組開始填充。 */
finished = 1;
return i; /* 返回「讀」到的位元組 */
} /* 當使用者向/proc檔案中寫時這個函式接收從使用者來的輸入。 */
#if linux_version_code >= kernel_version(2,2,0)
static ssize_t module_input(
struct file *file, /* 檔案自己 */
const char *buf, /* 存有輸入的緩衝區 */
size_t length, /* 緩衝區長度 */
loff_t *offset) /* 檔案偏移量--忽略 */
#else
static int module_input(
struct inode *inode, /* 檔案節點 */
struct file *file, /* 檔案自己 */
const char *buf, /* 存有輸入的緩衝區 */
int length) /* 緩衝區長度 */
#endif
{ int i;
/* 將輸入存入 message,module_output 將在以後能使用它。 */
for(i=0; i #if linux_version_code >= kernel_version(2,2,0)
get_user(message[i], buf+i);
/* 在 2.2 版中 get_user 的語義改變了,它不再返回乙個字元而是期待將乙個變數作為它的第一引數
* 和乙個使用者記憶體段的指標作為第二引數。
* * 這個改變的原因是在 2.2 版中, get_user 也可以讀短整型數或整數。它是通過使用sizeof來知道它將 * 讀到何種變數的,為此,它需要那個變數自身。*/
#else
message[i] = get_user(buf+i);
#endif
message[i] = '
Linux核心模組程式設計
目標 熟悉linux模組相關函式等 模組載入,例 static int init initialization function void module init initialization function 這裡 init define init attribute section init.t...
Linux核心模組程式設計
linux核心模組程式設計的資料有些紛繁複雜,有的過於簡單,有的過於龐雜,我試圖用筆記的形式想讀者展示怎樣來程序linux模組程式設計,力圖做到簡明扼要,這篇文章也是作為本人備忘的資料,所以有些地方過於簡略是難免的。本來這篇文章的目的就是讓使用者知其然,至於所以然還是請參考相應的資料,其實最好的資料...
將模組加入核心
可以去看看核心下面的驅動目錄,所有的目錄下面都會包含乙個makefile和乙個kconfig。這個kconfig的作用就是在執行make menuconfig的時候會從中讀出相應的選單,makefile自然是編譯指令碼了。我們準備在drivers目錄下面新增我們的hello模組,首先我們為我們的模組...