Linux核心模組的程式設計方法(轉)

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

  相信許多朋友和我一樣都在Linux環境下使用過C語言編過程式,其大多數都屬於使用者應用程式,也稱為普通使用者程式。寫了這麼多應用程式後,就有點兒想寫一點系統級的程式了,於是就參考了一些關於Linux核心程式設計原理的資料,並付之了實踐,現在就讓我將編寫核心模組的方法給大家介紹一下吧......

  一個Linux核心模組至少需要包括以下兩個函式:

  1.模組初始化函式——當模組被插入到Linux核心中時被呼叫;

  2.模組解除安裝函式——當模組從Linux核心中被解除安裝時被呼叫。

  一般來說,模組初始化函式給新模組在核心中註冊,並且得到一個呼叫控制程式碼;或者它使新模組的程式碼覆蓋原有的程式碼(通常情況下新模組的程式碼增加了一些新功能,然後呼叫原有的程式碼)。

  而模組解除安裝函式正好做了模組初始化函式相反的工作,它使新模組安全地被解除安裝。

  下面我們來看看如何在Linux核心中插入一個模組,讓其在螢幕上輸出“Hello , this is module speaking!”的字樣。

  程式檔案:hello.c

  #include $#@60;linux/kernel.h$#@62; /* 我們正在幹一些關於核心的事情 */

#include $#@60;linux/module.h$#@62; /* 具體來說,是在寫一個模組 */

#if CONFIG_MODVERSIONS==1 /* 如果需要指明模組的版本的話 */

#define MODVERSIONS

#include $#@60;linux/modversions.h$#@62; /* 那就將linux/modversions.h檔案包含*/

#endif

int init_module() /* 模組初始化函式 */

{

printk("Hello, this is the kernel speaking! n");

/* 如果我們將返回值置為非零,這說明初始化模組失敗 */

return 0;

}

void cleanup_module() /* 模組解除安裝函式 */

{

printk(“ This kernel module has been removed. n");

}

  為了編譯hello.c,我們還得編寫一個Makefile檔案。

  核心模組不是一個單獨的可執行體,它只能作為一個二進位制目標檔案(相當於DOS的obj檔案)被核心呼叫。經常在Linux下些程式的使用者一定熟悉cc或gcc的用法,在此我們使用GNU gcc來編譯hello.c檔案,使用-c標誌表示只將原始檔編譯成二進位制目標檔案。

  同時,所有的核心模組在編譯時都要使用__KERNEL__(注意,每一邊都是兩個半形的下劃線)標示,只有這樣才能告訴編譯器這個程式將在核心模式下執行而不是一個 ǖ撓沒Ы?獺?

  此外,在編譯時我們還得用到MODULE、LINUX兩個標示:

  MODULE — 告訴編譯器給核心模組一個合適的定義;

  LINUX —為了提高程式碼的可移植性,這裡說明我們的程式碼是在Linux作業系統下編譯。

  所以,Makefile如下:

  檔案:Makefile

  CC=gcc

MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX

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

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

  好了,接下來要做的事情就是以root的身份登入系統,或使用su來使自己成為root使用者。然後,使用insmod hello和rmmod hello 命令來插入或解除安裝hello模組。執行insmod hello命令後,hello模組被初始化,於是console輸出“Hello, this is the kernel speaking!”的字樣;同樣,執行rmsmod hello命令後,hello模組被解除安裝,於是系統要呼叫模組解除安裝函式來做清理工作,這時,console輸出“This kernel module has been removed.”的字樣。另外,一旦使用了insmod hello插入hello模組後,在/proc/modules目錄下將會有hello模組的紀錄。

  順便說一下,插入或解除安裝hello模組的動作不要在X-Windows下做,這是因為在程式中我們使用了printk函式,它能從核心直接向console控制檯輸出訊息,如果您使用的Linux的核心是不穩定的版本的話,使用X-Windows下的虛擬終端輸出可能會導致系統崩潰或重起。

  上面所說的是單個檔案的程式,如果程式比較大,則需要分解成多個原始檔。下面就來看看多個原始檔組成的核心模組程式是如何編寫、編譯的。

  我們需要做以下三個工作:

  1.在所有的原始檔中除了一個沒有,其他均要加上 #define __NO_VERSION__的宏定義。這一點非常重要,因為在module.h中已經包含了系統核心版本的定義,所有的核心模組都依照者這個已定義的包含了核心版本的全域性變數來編譯的。現在使用了#define __NO_VERSION__語句,可以在編譯時避免使用上述的全域性變數。但,此時您必須手工地包含version.h檔案,因為有了__NO_VERSION__的定義,module.h就不會自動地包含version.h檔案了;

  2.和平時一樣編譯所有的原始檔得到多個二進位制目標檔案;

  3.連線(link)所有的二進位制目標檔案,在X86硬體環境下使用這樣的命令: ld ?m elf_i386 ?r ?o $#@60;模組的名字$#@62;.o $#@60;原始檔1的名字$#@62;.o $#@60;原始檔2的名字$#@62;.o

  好,下面就舉一個例子。

  檔案:start.c

  /* 此檔案不包含__NO_VERSION__宏的定義 */

#include $#@60;linux/kernel.h$#@62; /* 說明我們在做一些關於核心的工作 */

#include $#@60;linux/module.h$#@62; /* 具體來說,是在寫一個模組 */

#if CONFIG_MODVERSIONS==1 /* 如果需要指明模組的版本的話 */

#define MODVERSIONS

#include $#@60;linux/modversions.h$#@62;

#endif

int init_module() /* 模組初始化函式 */

{

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

return 0;

}

  檔案:stop.c

  #include $#@60;linux/kernel.h$#@62; /*說明我們在做一些關於核心的工作*/

#define __NO_VERSION__ /* 禁止module.h使用含有核心版本號的全域性變數 */

/* 故,此宏的定義必須在包含module.h檔案之前 */

#include $#@60;linux/module.h$#@62; /* 包含module.h檔案 */

#include $#@60;linux/version.h$#@62; /* 由於__NO_VERSION__宏的定義,只能手工加入*/

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include $#@60;linux/modversions.h$#@62;

#endif

void cleanup_module() /* 模組解除安裝函式 */

{

printk("This kernel module has been removed. n");

}

  接下來就是編寫Makefile檔案了。

  檔案:Makefile

  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.

  下面的操作和使用單個原始檔的一樣,使用insmod hello和rmmod hello來裝卸hello模組了。

  關於Linux核心的程式設計有很多方面,涉及的知識點很多,需要程式設計師對C/C++甚至組合語言很熟悉,同時對作業系統原理也有一定的瞭解。如果能讀懂Linux的源程式那是更好了。雖然很辛苦,但是你一定會有收穫的。

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

相關文章