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 (LINUXKERNELPATH)M=(CURRENT_PATH)
modules

#clean

clean:

make -C (LINUXKERNELPATH)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

【ARM-Linux开发】Linux模块机制浅析的更多相关文章

  1. Linux模块机制浅析

    Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...

  2. Linux模块机制浅析_转

    Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ...

  3. webpack模块机制浅析【一】

    webpack模块机制浅析[一] 今天看了看webpack打包后的代码,所以就去分析了下代码的运行机制. 下面这段代码是webpack打包后的最基本的形式,可以说是[骨架] (function(roo ...

  4. 【Linux开发】Linux模块机制浅析

    Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! 我们通过创建一个简单的模块 ...

  5. Linux内核(6) - 模块机制与“Hello World!

    有一种感动,叫内牛满面,有一种机制,叫模块机制.显然,这种模块机制给那些Linux的发烧友们带来了方便,因为模块机制意味着人们可以把庞大的Linux内核划分为许许多多个小的模块.对于编写设备驱动程序的 ...

  6. 通过Anuglar Material串串学客户端开发 - NodeJS模块机制之Module.Exports

    module.exports 前文讲到在Angular Material的第二个编译文件docs/gulpfile.js中却看到了一个奇怪的东西module.exports那么module.expor ...

  7. 【linux驱动笔记】linux模块机制浅析

      1.   模块module 操作系统分微内核和宏内核,微内核优点,可以使操作系统仅作很少的事,其它事情如网络处理等都作为应用程序来实现,微内核精简的同时,必然带来性能的下降.而linux的宏内核设 ...

  8. 应聘linux/ARM嵌入式开发岗位

    **************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...

  9. linux下nginx模块开发入门

    本文模块编写参考http://blog.codinglabs.org/articles/intro-of-nginx-module-development.html 之前讲了nginx的安装,算是对n ...

随机推荐

  1. hbase与Hive的集成

    1 HBase与Hive的对比 1.Hive (1) 数据仓库 Hive的本质其实就相当于将HDFS中已经存储的文件在Mysql中做了一个双射关系,以方便使用HQL去管理查询. (2) 用于数据分析. ...

  2. Numpy | 06 从已有的数组创建数组

    numpy.asarray numpy.asarray 类似 numpy.array,但 numpy.asarray 参数只有三个,比 numpy.array 少两个. numpy.asarray(a ...

  3. python函数 | 列表生成式

    在编写程序或者查看别人的程序时,经常会遇到列表生成式,这个使用起来并不复杂,但是非常有用,使我们的代码更加简洁灵活.很多python使用者并不太会使用它.今天,就给大家详细讲解列表生成式和生成器表达式 ...

  4. 如何使用Microsoft的驱动程序验证程序解释无法分析的崩溃转储文件

    这篇文章解释了如何使用驱动程序验证工具来分析崩溃转储文件. 使用Microsoft驱动程序验证工具 如果您曾经使用Windows的调试工具来分析崩溃转储,那么毫无疑问,您已经使用WinDbg打开了一个 ...

  5. gtibase rpm包制作

    gitbase 是一个很不错的代码分析工具,我们可以直接使用sql来分析团队的代码,以下是rpm 包的制作 简单说明 rpm 包的制作使用了fpm 一个简单,方便可以跨平台的软件包制作工具 gitba ...

  6. call JSON.parse JSON.stringify typeof 的使用及严格模式this的使用

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. UmiJS可插拔的企业级 react 应用框架,配合ant-design-pro使用

    入门非常简单 # 安装 $ yarn global add umi # 或者 npm install -g umi # 新建应用 $ mkdir myapp && cd myapp # ...

  8. [WEB安全]源码泄露总结

    原文链接:https://blog.csdn.net/qq_36869808/article/details/88895109 源码泄露总结: svn源码泄露:https://blog.csdn.ne ...

  9. LCA的几种做法

    P3379 LCA $ 1:$蜗牛爬式 void dfs(int u,int fa) { f[u]=fa;//预处理father for(int i=head[u]; i; i=e[i].nxt) i ...

  10. EventHandler

    表示将处理不包含事件数据的事件的方法 作用:这句话的意思就是把这两个事放在一起了,意思就是叫你吃完饭了喊我一声.我委托你吃完饭了,喊我一声.这样我就不用过一会就来看一下你吃完了没有了,已经委托你了.