Linux核心之旅

來源:互聯網
上載者:User
 

核心模組是Linux核心向外部提供的一個插口,其全稱為動態可載入核心模組(Loadable Kernel Module,LKM),我們簡稱為模組。Linux核心之所以提供模組機制,是因為它本身是一個單核心(monolithic kernel)。單核心的最大優點是效率高,因為所有的內容都整合在一起,但其缺點是可擴充性和可維護性相對較差,模組機制就是為了彌補這一缺陷。

一、 什麼是模組

模組是具有獨立功能的程式,它可以被單獨編譯,但不能獨立運行。它在運行時被連結到核心作為核心的一部分在核心空間運行,這與運行在使用者空間的進程是不同的。模組通常由一組函數和資料結構組成,用來實現一種檔案系統、一個驅動程式或其他核心上層的功能。

二、 編寫一個簡單的模組

模組和核心都在核心空間運行,模組編程在一定意義上說就是核心編程。因為核心版本的每次變化,其中的某些函數名也會相應地發生變化,因此模組編程與核心版本密切相關。以下例子針對2.6核心

1.程式舉例

hellomod.c

// hello world driver for Linux 2.6 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h>/* 必要的標頭檔*/static int __init lkp_init( void ){printk(“<1>Hello,World! from the kernel space…\n”);return 0;}static void __exit lkp_cleanup( void ){printk(“<1>Goodbye, World! leaving kernel space…\n”); } module_init(lkp_init); module_exit(lkp_cleanup); MODULE_LICENSE(“GPL”);

 

.說明 
第4行:
        所有模組都要使用標頭檔module.h,此檔案必須包含進來。
第5行:
        標頭檔kernel.h包含了常用的核心功能。
第6行:
        標頭檔init.h包含了宏_init和_exit,它們允許釋放核心佔用的記憶體。
建議瀏覽一下該檔案中的代碼和注釋。
第9-12行:
        這是模組的初始化函數,它必需包含諸如要編譯的代碼、初始化資料結構等內容。
第11行使用了printk()函數,該函數是由核心定義的,功能與C庫中的printf()類似,
它把要列印的資訊輸出到終端或系統日誌。字串中的<1>是輸出的層級,
表示立即在終端輸出。
第15-18行:
        這是模組的退出和清理函數。此處可以做所有終止該驅動程式時相關的清理工作。
第20行:
        這是驅動程式初始化的進入點。對於內建模組,核心在引導時調用該進入點;
對於可載入模組則在該模組插入核心時才調用。
第21行:
        對於可載入模組,核心在此處調用module_cleanup()函數,而對於內建的模組,
它什麼都不做。
第22行:
        提示可能沒有GNU公用許可證。有幾個宏是在2.4版的核心中才開發的(詳情參見modules.h)。

  函數module_init()和cleanup_exit()是模組編程中最基本也是必須的兩個函數。
module_init()向核心註冊模組所提供的新功能,
而cleanup_exit()登出由模組提供的所有功能。

模組編程屬於核心編程,因此,除了對核心相關知識有所瞭解外,還需要瞭解與模組相關的知識。

1.應用程式與核心模組的比較
為了加深對核心模組的瞭解,表一給出應用程式與核心模組程式的比較。
表一 應用程式與核心模組程式的比較

  C語言應用程式 核心模組程式
使用函數 Libc庫 核心功能
Runspace 使用者空間 核心空間
運行許可權 普通使用者 超級使用者
入口函數 main() module_init()
出口函數 exit() module_exit()
編譯 Gcc –c Makefile
串連 Gcc insmod
運行 直接運行 insmod
調試 Gdb kdbug, kdb,kgdb等

從表一我們可以看出,核心模組程式不能調用libc庫中的函數,它運行在核心空間,且只有超級使用者可以對其運行。另外,模組程式必須通過module_init()和module-exit()函數來告訴核心“我來了”和“我走了”。

2.核心符號表(如果對以下第2~4點理解上有困難,可以越過)

如 前所述,Linux核心是一個整體結構,像一個圓球,而模組是插入到核心中的外掛程式。儘管核心不是一個可安裝模組,但為了方便起見,Linux把核心也看作 一個“母”模組。那麼模組與模組之間如何進行互動呢,一種常用的方法就是共用變數和函數。但並不是模組中的每個變數和函數都能被共用,核心只把各個模組中 主要的變數和函數放在一個特定的區段,這些變數和函數就統稱為符號。到低哪些符號可以被共用? Linux核心有自己的規定。對於核心這個特殊的母模組,在kernel/ksyms.c中定義了從中可以“移出”的符號,例如進程管理子系統可以“移出”的符號定義如下:

/* 進程管理 */

EXPORT_SYMBOL(do_mmap_pgoff);

EXPORT_SYMBOL(do_munmap);

EXPORT_SYMBOL(do_brk);

EXPORT_SYMBOL(exit_mm);

EXPORT_SYMBOL(schedule);

EXPORT_SYMBOL(jiffies);

EXPORT_SYMBOL(xtime);

你可能對這些變數和函數已經很熟悉。其中宏定義EXPORT_SYMBOL()本身的含義是“移出符號”。為什麼說是“移出”呢?因為這些符號本來是核心內部的符號,通過這個宏放在一個公開的地方,使得裝入到核心中的其他模組可以引用它們。

實際上,僅僅知道這些符號的名字是不夠的,還得知道它們在核心地址空間中的地址才有意義。因此,核心中定義了如下結構來描述模組的符號:

struct module_symbol

{

unsigned long value; /*符號在核心地址空間中的地址*/

const char *name; /*符號名*/

};

我們可以從/proc/ksyms檔案中讀取所有核心模組“移出”的符號,這所有符號就形成核心符號表,其格式如下:

記憶體位址 符號名 [所屬模組]

在模組編程中,可以根據符號名從這個檔案中檢索出其對應的地址,然後直接存取該地址從而獲得核心資料。第三列“所屬模組”指符號所在的模組名,對於從核心這一母模組移出的符號,這一列為空白。

模組載入後,2.4核心下可通過 /proc/ksyms、 2.6 核心下可通過/proc/kallsyms查看模組輸出的核心符號

3.模組依賴

如前所述,核心符號表記錄了所有模組可以訪問的符號及相應的地址。當一個新的模組被裝入核心後,它所申明的某些符號就會被登記到這個表中,而這些符號可能被其他模組所引用,這就引出了模組依賴這個問題。

一個模組A引用另一個模組B所移出的符號,我們就說模組B被模組A引用,或者說模組A相依模組B。如果要連結模組A,必須先連結模組B。這種模組間相互依賴的關係就叫模組依賴。

4.模組引用計數器

為 了確保模組安全地卸載,每個模組都有一個引用計數器。當執行模組所涉及的操作時就遞增計數器,在操作結束時就遞減這個計數器;另外,當模組B被模組A引用 時,模組B的引用計數就遞增,引用結束,計數器遞減。什麼時候可以卸載這個模組?當然只有這個計數器值為0的時候,例如,當一個檔案系統還被安裝在系統上 時就不能將其卸載,當這個檔案系統不再被使用時,引用計數器就為0,於是可以卸載。

四.模組編譯

Linux 中最重要的軟體開發工具是 GCC。GCC 是 GNU 的 C 和 C++ 編譯器。但是,在大型的開發項目中,通常有幾十到上百個的源檔案,如果每次均手工鍵入 gcc 命令進行編譯的話,則會非常不方便。因此,人們通常利用 make 工具來自動完成編譯工作。利用這種自動編譯可大大簡化開發工作,避免不必要的重新編譯。這些工作包括:如果僅修改了某幾個源檔案,則只重新編譯這幾個源檔案;如果某個標頭檔被修改了,則重新編譯所有包含該標頭檔的源檔案。

1.編譯工具make

實際上,make 工具通過一個稱為 Makefile 的檔案來完成並自動維護編譯工作。Makefile 需要按照某種文法進行編寫,其中說明了如何編譯各個源檔案並串連產生可執行檔,並定義了源檔案之間的依賴關係。下面給出2.6 核心模組的Makefile模板(請參看Makefile的寫法)

 

# Makefile2.6
obj-m += hellomod.o        # 產生hellomod 模組的目標檔案
CURRENT_PATH := $(shell pwd)   #模組所在的當前路徑
LINUX_KERNEL := $(shell uname -r)    #Linux核心原始碼的目前的版本
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) #Linux核心原始碼的絕對路徑
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules   #編譯模組了
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean    #清理

注意: 在每個命令前(例如make命令前)要鍵入一個定位字元(按TAB鍵產生)

有了Makefile,執行make命令,會自動形成相關的尾碼為.o和.ko檔案。
到此,模組編譯好了,該把它插入到核心了:
如:$insmod hellomod.ko

  當然,要以系統員的身份才能把模組插入。

  成功插入後,可以通過dmesg命令查看,螢幕最後幾行的輸出就是你程式中輸出的內容:Hello,World! from the kernel space…

 
當模組不再需要時,可以通過rmmod命令移去,例如

$rmmod hellomod

modutils是管理核心模組的一個軟體包。可以在任何獲得核心原始碼的地方擷取Modutils(modutils-x.y.z.tar.gz)原始碼,然後選擇最進階別的patch.x.y.z等於或小於當前的核心版本,安裝後在/sbin目錄下就會有insomod、rmmod、ksyms、lsmod、modprobe等公用程式。當然,通常我們在載入Linux核心時,modutils已經被載入。
1.Insmod命令
調用insmod程式把需要插入的模組以目標代碼的形式插入到核心中。在插入的時候,insmod自動調用init_module()函數運行。注意,只有超級使用者才能使用這個命令,其命令格式為:
# insmod  [path] modulename.c
2. rmmod命令
調用rmmod程式將已經插入核心的模組從核心中移出,rmmod會自動運行cleanup_module()函數,其命令格式為:
#rmmod  [path] modulename.c
3.lsmod命令
調用lsmod程式將顯示當前系統中正在使用的模組資訊。實際上這個程式的功能就是讀取/proc檔案系統中的檔案/proc/modules中的資訊,其命令格式為:
#lsmod
4.ksyms命令
ksyms這個程式用來顯示核心符號和模組符號表的資訊。與lsmod相似,它的功能是讀取/proc檔案系統中的另一個檔案/proc/kallsyms。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.