You are on page 1of 5

什么是内核模块?内核模块是一些可以让操作系统内核在需要时载入和执行的代码,这同

样意味着它可以在不需要时有操作系统卸载。它们扩展了操作系统内核的功能却不需要重
新启动系统。举例子来说,其中一种内核模块是设备驱动程序模块,它们用来让操作系统
正确识别,使用安装在系统上的硬件设备。如果没有内核模块,我们不得不一次又一次重
新编译生成单内核操作系统的内核镜像来加入新的功能。这还意味着一个臃肿的内核。
你 可 以 通 过 执 行 lsmod 命 令 来 查 看 内 核 已 经 加 载 了 哪 些 内 核 模 块 , 该 命 令 通 过 读
取/proc/modules 文件的内容来获得所需信息。
这些内核模块是如何被调入内核的?当操作系统内核需要的扩展功能不存在时,内核模块
管 理 守 护 进 程 kmod[1] 执 行 modprobe 去 加 载 内 核 模 块 。 两 种 类 型 的 参 数 被 传 递 给
modprobe:
一个内核模块的名字像 softdog 或是 ppp.
通用识别符像 char-major-10-30.
当传递给 modprobe 是通用识别符时,modprobe 首先在文件 /etc/modules.conf 查找该字符串。
如果它发现的一行别名像:
alias char-major-10-30 softdog

它就明白通用识别符是指向内核模块 softdog.o.
然后,modprobe 遍历文件 /lib/modules/version/modules.dep 来判断是否有其它内
核模块需要在该模块加载前被加载。该文件是由命令 depmod -a 建立,保存着内核模块的
依赖关系。举例来说,msdos.o 需要 fat.o 内核模块已经被内核载入。当要加载的内核模
块需要使用别的模块提供的符号链接时(多半是变量或函数),那么那些提供这些所需符
号链接的内核模块就被该模块所依赖。
最 终 , modprobe 调 用 insmod 先 加 载 被 依 赖 的 模 块 , 然 后 加 载 该 被 内 核 要 求 的 模 块 。
modprobe 将 insmod 指向 /lib/modules/version/[2]目录,该目录为默认标准存放内核
模块的目录。insmod 对内核模块存放位置的处理相当呆板,所以 modprobe 应该很清楚的知
道默认标准的内核模块存放的位置。所以,当你想要载入一个内核模块时,你可以执行:
insmod /lib/modules/2.5.1/kernel/fs/fat/fat.o
insmod /lib/modules/2.5.1/kernel/fs/msdos/msdos.o

或只是执行 "modprobe -a msdos"。


Linux 提供 modprobe, insmod and depmod 在一个名为 modutils 或 mod-utils 的工具包内。
在结束本章前,让我们来看一个/etc/modules.conf 文件:
#This file is automatically generated by update-modules
path[misc]=/lib/modules/2.4.?/local
keep
path[net]=~p/mymodules
options mydriver irq=10
alias eth0 eepro

用'#'起始的行为注释。空白行被忽略。
以 path[misc] 起始的行告诉 modprobe 用 /lib/modules/2.4.?/local 替代搜寻 misc
内核模块的路径。正如你看到的,命令解释器 shell 的元字符也可以使用。
以 path[net] 起始的行告诉 modprobe 在目录~p/mymodules 搜索网络方面的内核模块。
但是,在 path[net] 指令之前使用的"keep" 指令告诉 modprobe 只是将该路径添加到标准
搜索路径中,而不是像对待 misc 前面那样进行替换。
以 alias 起始的的行使 modprobe 加载 eepro.o 当 kmod 以通用识别符'eth0'要求加载相应
内核模块时。
你不会发现像"alias block-major-2 floppy"这样的别名行在文件 /etc/modules.conf 因为
modprobe 已经知道在绝大多数系统上安装的标准的设备的驱动模块。
现在你已经知道内核模块是如何被调入的了。当你想写你自己的依赖于其它模块的内核模
块时,还有一些内容没有提供。这个相对高级的问题将在以后的章节中介绍,当我们已经
完成前面的学习后。
在我们介绍源代码前,有一些事需要注意。系统彼此之间的不同会导致许多困难。顺利的编
译并且加载你的第一个"hello world"模块有时就会比较困难。但是当你跨过这道坎时,后面
会顺利的多。
1.2.1.1. 内核模块和内核的版本问题
为某个版本编译的模块将不能被另一个版本的内核加载如果内核中打开了 CONFIG_MODV
ERSIONS 选项。我们暂时不会讨论与此相关的内容。在我们进入相关内容前,本文档中的
范例可能在该选项打开的情况下无法工作。但是,目前绝大多数的发行版是将该选项打开
的。所以如果你遇到和版本相关的错误时,最好,重新编译一个关闭该选项的内核。
1.2.1.2. 使用 X 带来的问题
强烈建议你在控制台下输入文档中的范例代码,编译然后加载模块,而不是在 X 下。
模块不能像 printf() 那样输出到屏幕,但它们可以记录信息和警告,当且仅当你在使用
控制台时这些信息才能最终显示在屏幕上。 如果你从 xterm 中 insmod 一个模块,这些日志
信息只会记录在你的日志文件中。除了查看日志文件你将无法 得到输出信息。想要及时的获
得这些日志信息,建议所有的工作都在控制台下进行。
1.2.1.3. 编译相关和内核版本相关的问题
Linux 的发行版经常给内核打一些非标准的补丁,这种情况回导致一些问题的发生。
一个更普遍的问题是一些 Linux 发行版提供的头文件不完整。编译模块时你将需要非常多的
内核头文件。墨菲法则之一就是那些缺少的头文件恰恰是你最需要的。
我强烈建议从 Linux 镜像站点下载源代码包,编译新内核并用新内核启动系统来避免以上
的问题。参阅"Linux Kernel HOWTO"获得详细内容。
具有讽刺意味的是,这也会导致一些问题。gcc 倾向于在缺省的内核源文件路径(通常是
/usr/src/)。 这可以通过 gcc 的-I 选项来切换。

Automatically load kernel modules:


为搞清楚如何在系统启动时自动加载模块,搜索了好久,网上有很多人提出这个问题,但
都没有正确的答案,无论是中文社区还是英文社区,大家的回答都没有讲到点子上,无非
是围绕 modprobe.conf、modprobe 讲来讲去的,要不就是针对特定问题尝试不同的方法。有
的还建议把 modprobe modulename 写入 rc.local,却不曾想,rc.local 的执行被放在整个启动
顺序的很后面,而启动 init.d 下面定义的服务却在 rc.local 前面,那么如果某个服务要用这
个模块,就不行了。今天正好看到一篇这样的文章,转贴在这里:

rc.sysinit 中有这样的一段代码:

# Load other user-defined modules


for file in /etc/sysconfig/modules/*.modules ; do
[ -x $file ] && $file
done

# Load modules (for backward compatibility with VARs)


if [ -f /etc/rc.modules ]; then
/etc/rc.modules
fi

可见只需要配置两个地方的任何一个就可以了(以加载 fuse 内核模块为例)

(1) 在/etc/sysconfig/modules/下面创建*.modules 文件,参考已经有的*.modules 文件,例如


我写创建文件 my.modules,内容为 modprobe fuse
记得最后 chmod 755 my.modules

(2) 或者在/etc/rc.modules 里面加上 modprobe fuse,没有的话创建该文件。


然后 reboot,lsmod | grep fuse 验证一下就 OK 了。

在测试 LVS 时,因为我的 Fedora7 的 Kernel(2.6.21-1)缺省没有加载 ip_vs 模块,而内核中已


经 包 含 编 译 好 的 IPVS 相 关 的 模 块 了 , 放 在 : /lib/modules/2.6.21-
1.3194.fc7/kernel/net/ipv4/ipvs/下面,有:

/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_dh.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_ftp.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_lblc.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_lblcr.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_lc.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_nq.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_rr.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_sed.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_sh.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_wlc.ko
/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_wrr.ko

其中 ip_vs.ko 是 IPVS 的基本模块,不加载 IPVS 就不能工作(运行 ipvsadm 会报错的),而其


他的都是 IPVS 的调度算法或特定协议的辅助模块,需要时则须加载。
如果系统运行时手动加载则需:modprobe ip_vs 和 modprobe ip_vs_sh 等。
要了解如何在系统启动时自动加载模块(Automatically load kernel modules),就得先了解系
统是如阿启动的,启动的过程中按什么顺序做了什么,怎么做的,这些启动操作都有那些
文件和脚本控制。由于 Google 和 Baidu 出来的东西都解决不了问题,而且 man modprobe 和
man modprobe.conf 发现并不是需要修改的文件。

linux.vbird.org/”>http://linux.vbird.org/“开机关机流程与 Loader”:
1. 整个开机流程是
(1) 载入 BIOS 的硬件信息,并取得第一个开机装置的代号
(2)读取第一个开机装置的 MBR 的 boot Loader (grub)的开机信息
(3)载入 OS Kernel 信息,解压 Kernel,尝试驱动硬件
(4) Kernel 执行 init 程序并获得 run-lebel 信息(如 3 或 5)
(5) init 执行/etc/rc.d/rc.sysinit
(6)启动内核外挂模块(/etc/modprobe.conf)
(7) init 执行 run-level 的各种 Scripts,启动服务
(8) init 执行/etc/rc.d/rc.local
(9)执行/bin/login,等待用户 Login
(10)Login 后进入 Shell

看来正确的方式是把需要加载的模块放在 (5)或(6),但正如网络上很多人的尝试,修改
modprobe.conf 都 没 有 成 功 ( 例 如 在 modprobe.conf 中 增 加 install ip_vs…) 。 于 是 我 修 改
了/etc/rc.d/rc.sysinit 就成功加载了。

初步尝试在 rc.sysinit 最后增加 modprobe.conf ip_vs,重启后 lsmod | grep ip_vs,发现成功自


动加载了。

于是仿效 rc.sysinit 中其他模块的加载方法,扩展改脚本文件,在最后增加下来一段:


# load LVS IPVS modules
if [ -d /lib/modules/$unamer/kernel/net/ipv4/ipvs ]; then
for module in /lib/modules/$unamer/kernel/net/ipv4/ipvs/* ; do
module=${module##*/}
module=${module%.ko}
modprobe $module >/dev/null 2>&1
done
fi

就把/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/下的所有模块都自动加载了。其中:
if 语句检查 ipvs 模块的目录是否存在
for 循环遍历该目录下面的所有文件
module=${module##*/} :其中##表示从前面删除字符,*/表示删除到最后一个/,如果一个
#就表示只删除到第一个/。如果变量后面接##,表示在##后面的字符串取最长的(一直到最
后面),如果接#,表示取最小的一段。

module=${module%.ko}:表示从后面删除.ko。如果变量后面接%%,表示在%%后面的字符
串取最长的(一直到最前面),如果接%,表示取最小的一段。
这样多 module 的两次修改就得到了模块名,就是文件名不带路径和.ko 后缀。
modprobe $module >/dev/null 2>&1:加载模块,输出都指向空设备
这样重启后 lsmod | grep ip_vs 就会得到:

ip_vs_wrr 6977 0
ip_vs_wlc 6081 0
ip_vs_sh 6593 0
ip_vs_sed 6081 0
ip_vs_rr 6081 0
ip_vs_nq 5953 0
ip_vs_lc 5953 0
ip_vs_lblcr 10565 0
ip_vs_lblc 9797 0
ip_vs_ftp 10053 0
ip_vs_dh 6593 0

ip_vs 79425 22
ip_vs_wrr,ip_vs_wlc,ip_vs_sh,ip_vs_sed,ip_vs_rr,ip_vs_nq,ip_vs_lc,ip_vs_lblcr,ip_vs_

You might also like