国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

Linux模塊機制淺析

2024-06-28 13:26:21
字體:
來源:轉載
供稿:網友
linux模塊機制淺析 2014-05-15 21:47 by Florian, ... 閱讀, ... 評論, 收藏, 編輯

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-genericM=/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將模塊卸載。

結論

如此看來,內核模塊其實并不神秘。傳統的用戶程序需要編譯為可執行程序才能執行,而模塊程序只需要編譯為目標文件的形式便可以加載到內核,有內核實現模塊的鏈接,將之轉化為可執行代碼。同時,在內核加載和卸載的過程中,會通過函數回調用戶定義的模塊入口函數和模塊出口函數,實現相應的功能。

參考資料

http://hi.baidu.com/20065562/item/15dcc4ce92c3d510b67a24af

http://blog.chinaunix.net/uid-26009923-id-3840337.html


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东源县| 开原市| 沭阳县| 正阳县| 潜山县| 苍山县| 宜兴市| 黑龙江省| 普定县| 石楼县| 巴塘县| 青阳县| 昂仁县| 秦安县| 巴林右旗| 于田县| 马边| 浙江省| 汽车| 新竹市| 库尔勒市| 龙口市| 金塔县| SHOW| 襄樊市| 射洪县| 扬中市| 安仁县| 鹤山市| 娄底市| 山东省| 揭阳市| 宾阳县| 林甸县| 方城县| 万宁市| 抚顺市| 子洲县| 县级市| 闸北区| 湖口县|