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将模块卸载。

结论

如此看来,内核模块其实并不神秘。传统的用户程序需要编译为可执行程序才能执行,而模块程序只需要编译为目标文件的形式便可以加载到内核,有内核实现模块的链接,将之转化为可执行代码。同时,在内核加载和卸载的过程中,会通过函数回调用户定义的模块入口函数和模块出口函数,实现相应的功能。

参考资料

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

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

Linux模块机制浅析相关推荐

  1. Linux系列之soft lockup机制 浅析

    Linux系列之soft lockup机制 浅析 1.背景 2.什么是lockup? 2.1 lockup检测机制 2.2 softlockup的工作原理 3.soft lockup机制分析 3.1 ...

  2. 从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(一)

    从 2.4 到 2.6:Linux 内核可装载模 块机制的改变对设备驱动的影响 <?xml:namespace prefix = o ns = "urn:schemas-microso ...

  3. 在linux c 以结构体形式写文件 结构体参数如何在函数中传递,Linux安全审计机制模块实现分析(16)-核心文件之三auditsc.c文件描述及具体变量、函数定义...

    原标题:Linux安全审计机制模块实现分析(16)-核心文件之三auditsc.c文件描述及具体变量.函数定义 2.4.3文件三auditsc.c2.4.3.1 文件描述 kernel/auditsc ...

  4. linux注册函数机制,Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动...

    原标题:Linux可信计算机制模块详细分析之函数实现机制(1)字符设备驱动 2.3 函数实现机制 2.3.1 Linux 字符设备驱动 在linux 3.5.4中,用结构体cdev描述字符设备,cde ...

  5. Linux内核品读 /基础组件/ 模块机制快速入门

    哈喽,我是杰克吴,继续记录我的学习心得. 一.关于兴趣的几点思考 1. 享受不是兴趣,愿意付出才是: 兴趣很容易跟享受混淆.享受是被动的,无需付出:而兴趣则要求你甘愿为了这件事情付出努力. 2.任何事 ...

  6. linux软中断是什么机制,Linux软中断原理浅析

    Linux软中断原理浅析 Linux软中断原理浅析 Linux中的软中断机制用于中对时间要求最严格以及最重要的中断下半部进行使用.在系统设计过 程中,大家都清楚中断上下文不能处理太多的事情,需要快速的 ...

  7. Linux内核编程01:模块机制

    模块机制是Linux内核非常重要的一个功能,如果从C语言的模块化编程角度来看,Linux内核其实就是由大大小小.层次不同的模块构成的.有的模块比较小,一个C源文件就实现了:有的模块比较复杂,由多个源文 ...

  8. Smack核心机制浅析

    Smack核心机制浅析 在以上几篇博客中,我已经阐述了有关Smack技术的一些知识和要点,但是没有把Smack最精华的部分展示出来,由于本人学疏才浅,在阐述的过程中不免有这样那样的问题和诟病,但本人还 ...

  9. Linux内核bridge浅析

    Linux内核bridge浅析 Linux网桥模型: Linux内核通过一个虚拟的网桥设备来实现桥接的,这个设备可以绑定若干个以太网接口设备,从而将它们桥接起来.如下图所示: 网桥设备br0绑定了et ...

最新文章

  1. Windows删除文件时显示找不到该项目
  2. Servlet Cookie处理
  3. ajax查询返回字符串,从CFC返回结果之前,对AJAX查询执行字符串函数
  4. PrepareStatement 和Statement 的区别?
  5. java8 streams_Java 8 Friday:使用Streams API时的10个细微错误
  6. sql 链接到oracle数据库,通过MSSQL连接服务器连接至Oracle数据库
  7. 计算机用手机的网络,电脑做热点让手机上网_电脑开热点给手机用
  8. 我的开发笔记spring等常见问题
  9. 华为 2017 秋季校招笔试题(C++ 后台)
  10. 数据-第11课-双向链表
  11. opencv提取图像的缺陷并用最小矩形框出来
  12. 字典爆破php,密码字典 渗透测试字典 爆破字典
  13. 科学计算机又称6,计算机应用基础试题2
  14. 专利欠费怎么办?教你在网上缴纳专利年费,全网最详细步骤
  15. Feescale K60开发笔记3: Tftpd32的使用
  16. N,N-二甲基十二烷基胺(CAS 112-18-5)的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  17. php爬取百度关键词时出现,百度安全验证,解决方法
  18. Android无线热点默认属性修改
  19. SIGABRT的可能原因
  20. JavDroider的作品展示

热门文章

  1. ping连接linux无法访问目标主机,无法访问目标主机是什么情况【解决方法】
  2. 计算机应用计算题(88)10,计算机应用考试习题(88页)-原创力文档
  3. ubuntu安装ffmpeg_安装 Jellyfin 开始肥宅生活
  4. 5 java中的集合类_java基础(5)-集合类1
  5. 计算机控制系统如何设计,计算机控制系统设计
  6. char栈java,Java数据结构与算法-栈和队列(示例代码)
  7. jq步骤条 java_JQuery实现步骤条效果(上一步,下一步)
  8. Linux下的samba服务配置详解
  9. Linux下的shell脚本实战之用户创建
  10. linux x下载工具,Linux下强大的Axel下载工具