Linux にモジュール メカニズムを導入する利点: 1. アプリケーションが終了するとき、リソースの解放やその他のクリーンアップ作業を無視できますが、モジュールの終了関数は初期化によって行われたすべてを慎重に元に戻す必要があります。機能; 2 、このメカニズムはモジュールの開発サイクルを短縮するのに役立ちます、つまり、登録とアンインストールが非常に柔軟で便利です。
#このチュートリアルの動作環境: linux7.3 システム、Dell G3 コンピューター。
まず、モジュールは将来のリクエストに対応するために自身を事前登録し、その後、その初期化関数がすぐに終了します。言い換えれば、モジュール初期化関数のタスクは、将来の関数呼び出しに備えて事前に準備することです。
利点:
1) アプリケーションが終了するとき、リソースの解放やその他のクリーンアップ作業を無視できますが、モジュールの終了関数はアプリケーションを慎重に初期化解除する必要があります。 . 関数が実行するすべてのこと。
2) このメカニズムは、モジュール開発サイクルの短縮に役立ちます。つまり、登録とアンインストールは非常に柔軟で便利です。
Linux では、ユーザーがモジュールを挿入することでカーネルに介入できます。 Linux のモジュール メカニズムは長い間十分に明確になっていませんでした。そのため、この記事ではカーネル モジュールのロード メカニズムを簡単に分析します。
モジュールの Hello World!
簡単なモジュールを作成してテストします。 1 つ目はソース ファイル 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
モジュールのデータ構造
まず、モジュールのカーネル データ構造を理解しましょう。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. ..) 実装。これには 2 つの主要な関数呼び出しがあります。 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 関数には 4 つの主要な関数があります電話。 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);//卸载模块 …… }
コールバック出口を通じてモジュールのエクスポート関数関数を完了し、最後に free_module を呼び出してモジュールをアンインストールします。
#結論
カーネルモジュールは謎ではないようです。従来のユーザー プログラムは実行前に実行可能プログラムにコンパイルする必要がありますが、モジュール プログラムはオブジェクト ファイルにコンパイルしてからカーネルにロードするだけで済み、カーネルはモジュールへのリンクを実装し、実行可能コードに変換します。同時に、カーネルのロードおよびアンロードのプロセス中に、ユーザー定義のモジュール入口関数とモジュール出口関数が関数を通じてコールバックされ、対応する関数が実装されます。 関連する推奨事項: 「Linux ビデオ チュートリアル 」
以上がLinuxにモジュールメカニズムを導入する利点は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。