本文简介
Linux 设备驱动以内核模块的形式出现,因此,学会编写 Linux 内核模块是学习 Linux 设备驱动的先决条件。
4.1~4.2节讲解了Linux内核模块的概念和结构,4.3~4.8节对Linux内核模块的各个组成部分进行了详细讲解,4.1~4.2节与4.3~4.8节是整体与部分的关系。
4.9节讲解了独立存在的Linux内核模块的Makefile文件编写方法和模块的编译方法。
4.1 Linux 内核模块简介
Linux 内核的整体结构非常庞大, 其包含的组件也非常多。 我们怎样把需要的部分都包含在内核中呢?一种方法是把所有需要的功能都编译到 Linux 内核。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。
有没有一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码可被动态地加载到内核中呢?
Linux 提供了这样的一种机制,这种机制被称为模块(Module),可以实现以上效果。模块具有以下特点。
? 模块本身不被编译入内核映像,从而控制了内核的大小。
? 模块一旦被加载,它就和内核中的其他部分完全一样。
为了使读者对模块建立初步的感性认识,我们先来看一个最简单的内核模块“Hello World”,如代码清单 4.1 所示。
代码清单 4.1 一个最简单的 Linux 内核模块
#include <linux/init.h>
#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void){printk(KERN_ALERT " Hello World enter\n");return 0;
}static void hello_exit(void){printk(KERN_ALERT " Hello World exit\n ");
}module_init(hello_init);
module_exit(hello_exit);MODULE_AUTHOR("santiren");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
这个最简单的内核模块只包含内核模块加载函数、卸载函数和对 Dual BSD/GPL 许可权限的声明以及一些描述信息。编译它会产生 hello.ko 目标文件,通过“insmod hello.ko”命令可以加载它,通过“rmmod hello”命令可以卸载它,加载时输出“Hello World enter”,卸载时输出“Hello World exit”。
内核模块中用于输出的函数是内核空间的 printk()而非用户空间的 printf(), printk()的用法和 printf()相似,但前者可定义输出级别。 printk()可作为一种最基本的内核调试手段。
在 Linux 系统中,使用 lsmod 命令可以获得系统中加载了的所有模块以及模块间的依赖关系,例如:
[root@localhost driver_study]# lsmod
Module Size Used by
hello 1568 0
ohci1394 32716 0
ide_scsi 16708 0
ide_cd 39392 0
cdrom 36960 1 ide_cd
lsmod 命令实际上读取并分析/proc/modules 文件,与上述 lsmod 命令结果对应的/proc/modules 文件如下:
[root@localhost driver_study]# cat /proc/modules
hello 1568 0 - Live 0xc8859000
ohci1394 32716 0 - Live 0xc88c8000
ieee1394 94420 1 ohci1394, Live 0xc8840000
ide_scsi 16708 0 - Live 0xc883a000
ide_cd 39392 0 - Live 0xc882f000
cdrom 36960 1 ide_cd, Live 0xc8876000
内核中已加载模块的信息也存在于/sys/module 目录下,加载 hello.ko 后,内核中将包含/sys/module/hello目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在/sys/module/hello 目录下运行“tree –a”得到如下目录树:
[root@localhost hello]# tree -a
.
|-- refcnt
'-- sections|-- .bss|-- .data|-- .gnu.linkonce.this_module|-- .rodata|-- .rodata.str1.1|-- .strtab|-- .symtab|-- .text'-- _ _versions
modprobe 命令比 insmod 命令要强大,它在加载某模块时会同时加载该模块所依赖的其他模块。使用modprobe 命令加载的模块若以“modprobe -r filename”的方式卸载将同时卸载其依赖的模块。
使用 modinfo <模块名>命令可以获得模块的信息,包括模块的作者、模块的说明、模块所支持的参数以及 vermagic,如下所示:
[root@localhost driver_study]# modinfo hello.ko
filename: hello.ko
license: Dual BSD/GPL
author: santiren
description: A simple Hello World Module
alias: a simplest module
vermagic: 2.6.15.5 686 gcc-3.2
depends:
4.2 Linux 内核模块的程序结构
一个 Linux 内核模块主要由以下几个部分组成。