共計 5032 個字符,預計需要花費 13 分鐘才能閱讀完成。
本文丸趣 TV 小編為大家詳細介紹“linux 中引入模塊機制的好處是什么”,內容詳細,步驟清晰,細節處理妥當,希望這篇“linux 中引入模塊機制的好處是什么”文章能幫助大家解決疑惑,下面跟著丸趣 TV 小編的思路慢慢深入,一起來學習新知識吧。
linux 中引入模塊機制的好處:1、應用程序在退出時,可以不管資源的釋放或者其他的清除工作,但是模塊的退出函數卻必須仔細此撤銷初始化函數所作的一切;2、該機制有助于縮短模塊的開發周期,即注冊和卸載都很靈活方便。
Linux 中引入模塊機制有什么好處?
首先,模塊是預先注冊自己以便服務于將來的某個請求,然后他的初始化函數就立即結束。換句話說,模塊初始化函數的任務就是為以后調用函數預先作準備。
好處:
1) 應用程序在退出時,可以不管資源的釋放或者其他的清除工作,但是模塊的退出函數卻必須仔細此撤銷初始化函數所作的一切。
2) 該機制有助于縮短模塊的開發周期。即:注冊和卸載都很靈活方便。
Linux 模塊機制淺析
Linux 允許用戶通過插入模塊,實現干預內核的目的。一直以來,對 linux 的模塊機制都不夠清晰,因此本文對內核模塊的加載機制進行簡單地分析。
模塊的 Hello World!
我們通過創建一個簡單的模塊進行測試。首先是源文件 main.c 和 Makefile。
florian@florian-pc:~/module$ cat main.c
#include linux/module.h
#include linux/init.h
static int __init init(void)
{
printk(Hi module!\n
return 0;
}
static void __exit exit(void)
{
printk(Bye module!\n
}
module_init(init);
module_exit(exit);
其中 init 為模塊入口函數,在模塊加載時被調用執行,exit 為模塊出口函數,在模塊卸載被調用執行。
florian@florian-pc:~/module$ cat Makefile
obj-m += main.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
其中,obj- m 指定了目標文件的名稱,文件名需要和源文件名相同(擴展名除外),以便于 make 自動推導。
然后使用 make 命令編譯模塊,得到模塊文件 main.ko。
florian@florian-pc:~/module$ make
make -C /usr/src/linux-headers-2.6.35-22-generic M=/home/florian/module modules
make[1]: 正在進入目錄 `/usr/src/linux-headers-2.6.35-22-generic
Building modules, stage 2.
MODPOST 1 modules
make[1]: 正在離開目錄 `/usr/src/linux-headers-2.6.35-22-generic
使用 insmod 和 rmmod 命令對模塊進行加載和卸載操作,并使用 dmesg 打印內核日志。
florian@florian-pc:~/module$ sudo insmod main.ko;dmesg | tail -1
[31077.810049] Hi module!
florian@florian-pc:~/module$ sudo rmmod main.ko;dmesg | tail -1
[31078.960442] Bye module!
通過內核日志信息,可以看出模塊的入口函數和出口函數都被正確調用執行。
模塊文件
使用 readelf 命令查看一下模塊文件 main.ko 的信息。
florian@florian-pc:~/module$ readelf -h main.ko
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2 s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1120 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 19
Section header string table index: 16
我們發現 main.ko 的文件類型為可重定位目標文件,這和一般的目標文件格式沒有任何區別。我們知道,目標文件是不能直接執行的,它需要經過鏈接器的地址空間分配、符號解析和重定位的過程,轉化為可執行文件才能執行。
那么,內核將 main.ko 加載后,是否對其進行了鏈接呢?
模塊數據結構
首先,我們了解一下模塊的內核數據結構。
linux3.5.2/kernel/module.h:220
struct module
{
……
/* Startup function. */
int (*init)(void);
……
/* Destruction function. */
void (*exit)(void);
……
};
模塊數據結構的 init 和 exit 函數指針記錄了我們定義的模塊入口函數和出口函數。
模塊加載
模塊加載由內核的系統調用 init_module 完成。
linux3.5.2/kernel/module.c:3009
/* This is where the real work happens */
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs)
{
struct module *mod;
int ret = 0;
……
/* Do all the hard work */
mod = load_module(umod, len, uargs);// 模塊加載
……
/* Start the module */
if (mod- init != NULL)
ret = do_one_initcall(mod- init);// 模塊 init 函數調用
……
return 0;
}
系統調用 init_module 由 SYSCALL_DEFINE3(init_module…) 實現,其中有兩個關鍵的函數調用。load_module 用于模塊加載,do_one_initcall 用于回調模塊的 init 函數。
函數 load_module 的實現為。
linux3.5.2/kernel/module.c:2864
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static struct module *load_module(void __user *umod,
unsigned long len,
const char __user *uargs)
{
struct load_info info = {NULL,};
struct module *mod;
long err;
……
/* Copy in the blobs from userspace, check they are vaguely sane. */
err = copy_and_check(info, umod, len, uargs);// 拷貝到內核
if (err)
return ERR_PTR(err);
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info);// 地址空間分配
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}
……
/* Fix up syms, so that st_value is a pointer to location. */
err = simplify_symbols(mod, info);// 符號解析
if (err 0)
goto free_modinfo;
err = apply_relocations(mod, info);// 重定位
if (err 0)
goto free_modinfo;
……
}
函數 load_module 內有四個關鍵的函數調用。copy_and_check 將模塊從用戶空間拷貝到內核空間,layout_and_allocate 為模塊進行地址空間分配,simplify_symbols 為模塊進行符號解析,apply_relocations 為模塊進行重定位。
由此可見,模塊加載時,內核為模塊文件 main.ko 進行了鏈接的過程!
至于函數 do_one_initcall 的實現就比較簡單了。
linux3.5.2/kernel/init.c:673
int __init_or_module do_one_initcall(initcall_t fn)
{
int count = preempt_count();
int ret;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn();// 調用 init module
……
return ret;
}
即調用了模塊的入口函數 init。
模塊卸載
模塊卸載由內核的系統調用 delete_module 完成。
linux3.5.2/kernel/module.c:768
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
{
struct module *mod;
char name[MODULE_NAME_LEN];
int ret, forced = 0;
……
/* Final destruction now no one is using it. */
if (mod- exit != NULL)
mod- exit();// 調用 exit module
……
free_module(mod);// 卸載模塊
……
}
通過回調 exit 完成模塊的出口函數功能,最后調用 free_module 將模塊卸載。
讀到這里,這篇“linux 中引入模塊機制的好處是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注丸趣 TV 行業資訊頻道。
向 AI 問一下細節
丸趣 TV 網 – 提供最優質的資源集合!