Linux 内核模块

1 Linux 内核模块简介

Linux 内核是一个十分庞大的系统,如何能够为其瘦身,订制适合自己应用场景的 linux 系统,这就需要了解模块技术。

linux 内核与各模块的关系分为两种:

  • 把所有需要的功能都直接编译进入内核,但这会带来两个问题,一是生成的内核很大,从而导致内核占用内存也很大;二是如果内核需要新增或者删除功能的话,必须要重新编译内核。
  • 编出的内核本身不需要包含所有功能,而这些功能在需要使用时动态加载到内核,如此动态加载的功能单元就叫做模块。

Linux 提供的这种机制被称为模块,模块具有这样的特点:

  • 模块本身不被编译进入内核,从而控制了内核的大小。
  • 模块一旦被加载,它就和内核中的其它部分完全一样。

2 模块样例

以下是一个样例模块 hello.ko 样例:

/*
 *a simple kernel module: hello
 *
 *Licensed under GPLv2 or later
 */

#include <linux/init.h>
#include <linux/module.h>

static int __init hello_init(void)
{
    printk(KERN_INFO "Hello World enter.\n");
    return 0;
}

module_init(hello_init);

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Hello world exit.\n");
}

module_exit(hello_exit);

MODULE_AUTHOR("penghuster <penghuster@163.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple Hello World Module.");
MODULE_ALIAS("a simplest module.");

编译此模块的方法可以分为两种方式:

2.1 与内核编译同步捆绑进行

在内核的相关功能单元(如 drivers)下创建 hello 文件夹,然后根据内核Kconfig 和 Makefile的编写规则,编写 hello 模块的 Kconfig 和 Makefile 文件。
drivers/hello/Kconfig:

#
# Hello driver configuration 
#

#menu "HELLO driver"
comment "hello driver"

config CONFIG_HELLO
    bool "support HELLO"

#endmenu

drivers/hello/Makefile:

#drivers/test/test/Makefile
#
#Makefile for the TEST CPU
#

obj-$(CONFIG_HELLO) += hello.o

另外在drivers/Kconfig文件中添加行 source "drivers/hello/Kconfig"
在drivers/Makefile文件中添加 obj-y += hello/

注意:hello/Makefile也可直接写为 obj-m += hello.oobj-y += hello.o 从而可以省略 Kconfig的相关工作。Kconfig是为了 make menuconfig 配置而添加。

2.2 独立编译模块

需要进行独立模块编译前,需要确认 /lib/modules/目录下是否已经安装你需要进行模块编译的内核版本。如果没有,请安装对应版本的 linux-image 和 Linux-image-extra 包,具体命令如下:

sudo apt-get install --reinstall linux-image-3.13.0-34-generic
sudo apt-get install --reinstall linux-image-extra-3.13.0-34-generic`

然后编写如下Makefile:

##此处表示编写在本机上使用的,交叉编译器进行交叉编译,此处也需要改变。
KVERS = $(shell uname -r) 

obj-m += hello.o

#Specify flags for the module compilation
#EXTRA_CFLAGS = -g -o0

build: kernel_modules

kernel_modules:
    make -C /lib/modules/$(KVERS)/build M=$(PWD) modules
    
clean:
    make -C /lib/modules/$(KVERS)/build M=$(PWD) clean

2.3 内核模块的相关操作命令

insmod ./hello.ko ---加载模块
rmmod hello ---卸载模块
lsmod / cat /proc/modules ---查看所有模块
/sys/module/hello 下执行 tree -a ---查看模块的详细组成文件及其信息
/lib/modules/<kernel-version>/modules.dep ---文件查看模块依赖
modinfo hello.ko ---查看模块信息
modprobe ./hello.ko ---加载该模块的同时,加载该模块的所有依赖模块
modprobe -r hello ---卸载该模块,同时卸载其依赖模块

3 Linux 内核模块的程序结构

3.1 模块加载函数

初始化函数原型

statitc int __init initialization_function(void)
{
    /* 初始化代码*/
}
module_init(initialization_function);

内核中也可以主动调用以下函数来灵活加载模块
request_module(module_name);
在 Linux ,所有标识为 __init 的函数如果直接编译进入内核,成为内核镜像的一部分,在连接的时候都会放到 .init.text 段区。

注意:很明显模块通过 insmod 加载,并没有编入到内核镜像中,两者是通过MODULE宏变量来实现是否编入内核。
#ifdef MODULE
#define module_init(initfn) static inline initcall_t __inittest(void)
{ return initfn; }
#else
#define module_init(x) __initcall(x);
#endif

#define __init __attribute__((__section__(".init.text")))
所有的 __init 函数在区段 .initcall.init 中保存了一份函数指针,在初始化时内核会通过这些函数指针调用这些 __init 函数,并在初始化完成后,释放 init区段(包括 .init.text / .initcall.init)的内存.
除函数外数据也可以定义为 __initdata. 只有初始化阶段需要的函数.

3.2 模块卸载函数

函数原型

static void __exit cleanup_function(void)
{
    //释放代码
}
module_exit(cleanup_function);

同样也存在 __exitdata 只在退出阶段可用的数据。

3.3 模块参数

我们可以使用 "module_param(param_name, param_type, r/w)" 为模块定义一个参数。例如:

static char *book_name = "dissecting Linux device Driver";
module_param(book_name, charp, S_IRUGO);

static int book_num = 4000;
module_param(book_num, int, S_IRUGO);

其参数类型可以是 byte, short, ushort, int, uint, long, ulong, charp(char *), bool 或 invbool(布尔的反)。

模块参数的传递,用户可以在加载模块时使用形式为 insmod(/modprbe) module_name param_name = praram_value,如果模块被内置,则使用 bootloader 通过在 bootargs 里设置 “模块名.参数名 = 值” 值得形式传递给内核。

另外也可使用 “module_param_array(array_name, array_type, array_length, r/w)” 来传递数组参数。运行 insmod 或 modprobe 时,应该使用逗号分隔开数组元素。

模块加载成功后,在 /sys/module/ 目录下会出现以此模块名命名的目录。当参数的读写属性为 0 时,标示在 sysfs 文件系统下,不存在对应的文件节点;当模块存在 “参数读写属性” 不为 0 的命令行参数时, 此模块目录下将出现 parameters 目录,此目录下将出现与参数同名的文件,其文件的读写属性与参数一致。

3.4 导出符号

Linux 的 “/proc/kallsyms” 文件对应着内核符号表,它记录了符号以及符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号中:

EXPORT_SYMBOL(symbol_name);
EXPORT_SYMBOL_GPL(symbol_name);

导出的符号可以被其它模块使用,只需要使用前申明下即可。EXPORT_SYMBOL_GPL 只适用于包含 GPL 许可权的模块。

3.5 模块声明与描述

Linux 内核模块的一般信息声明

MODULE_AUTHOR("penghuster <penghuster@163.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple Hello World Module.");
MODULE_ALIAS("a simplest module.");
MODULE_DEVICE_TABLE(table_info);

对于 USB和PCI等设备,通常会创建一个 MODULE_DEVICE_TABLE,已表明该驱动模块所支持的设备。

3.6 模块的使用计数

Linux 2.4 内核中,模块自身通过 MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT 宏来管理自己被使用的计数。

Linux 2.6 以后内核提供了模块计数管理接口 try_module_get(&module)则增加使用计数函数 和 module_put(&module) 减少使用计数函数,从而取代 linux 2.4 的方式。只有当模块使用计数为 0 时,方能够卸载模块。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容