Linux设备驱动开发详解-Note(7)---Linux 内核模块(1)

时间:2021-02-26 17:56:42

Linux 内核模块(1)

成于坚持,败于止步

Linux 内核模块简介 

Linux 内核的整体结构非常庞大,其包含的组件也非常多。我们怎样把需要的部分都包含在内核中呢? 

一种方法是把所有需要的功能都编译到 Linux 内核。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。 

有没有一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码可被动态地加载到内核中呢? 

Linux 提供了这样的一种机制,这种机制被称为模块(Module),可以实现以上效果。模块具有以下特点。 

模块本身不被编译入内核映像,从而控制了内核的大小。 

模块一旦被加载,它就和内核中的其他部分完全一样。 

为了使读者对模块建立初步的感性认识,我们先来看一个最简单的内核模块“Hello World”,如代码所示:

1  #include <linux/init.h> 
2 #include <linux/module.h>
3 MODULE_LICENSE("Dual BSD/GPL");
4 static int hello_init(void)
5 {
6 printk(KERN_ALERT " Hello World enter\n");
7 return 0;
8 }
9 static void hello_exit(void)
10 {
11 printk(KERN_ALERT " Hello World exit\n ");
12 }
13 module_init(hello_init);
14 module_exit(hello_exit);
15
16 MODULE_AUTHOR("Song Baohua");
17 MODULE_DESCRIPTION("A simple Hello World Module");
18 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: Song Baohua
description: A simple Hello World Module
alias: a simplest module
vermagic: 2.6.15.5 686 gcc-3.2
depends:

Linux 内核模块的程序结构 

一个 Linux 内核模块主要由以下几个部分组成。 

模块加载函数(必须)。 

当通过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。 

模块卸载函数(必须)。 

当通过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。 

模块许可证声明(必须)。 

模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染 (kernel tainted)的警告。 在 Linux 2.6 内核中,可接受的 LICENSE 包括“GPL”、“GPL v2”、“GPL and additional rights”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”。 大多数情况下,内核模块应遵循 GPL 兼容许可权。Linux 2.6 内核模块最常见的是以 MODULE_LICENSE( "Dual BSD/GPL" ) 语句 声 明模 块采用 BSD/GPL 双LICENSE。 

模块参数(可选)。 

模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。 

模块导出符号(可选)。 

内核模块可以导出符号(symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数。 

模块作者等信息声明(可选)。

就到这里了,O(∩_∩)O~

我的专栏地址:http://blog.csdn.net/column/details/linux-driver-note.html

待续。。。。