1.内核模块
时间:2006-09-17 来源:pgpxc
内核模块
1.1. 什么是内核模块?
现在,你是不是想编写内核模块。你应该懂得C语言,写过一些用户程序,那么现在你将要见识一些真实的东西。在这里,你会看到一个野蛮的指针是如何毁掉你的文件系统的,一次内核崩溃意味着重启动。 什么是内核模块?内核模块是一些可以让操作系统内核在需要时载入和执行的代码,这同样意味着它可以在不需要时有操作系统卸载。它们扩展了操作系统内核的功能却不需要重新启动系统。举例子来说,其中一种内核模块时设备驱动程序模块,它们用来让操作系统正确识别,使用安装在系统上的硬件设备。如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能。这还意味着一个臃肿的内核。 1.2. 内核模块是如何被调入内核工作的? 你可以通过执行 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已经知道在绝大多数系统上安装的标准的设备的驱动模块。 现在你已经知道内核模块是如何被调入的了。当你想写你自己的依赖于其它模块的内核模块时,还有一些内容没有提供。这个相对高级的问题将在以后的章节中介绍,当我们已经完成前面的学习后。 1.2.1. 在开始前 在我们介绍源代码前,有一些事需要注意。系统彼此之间的不同会导致许多困难。顺利的编译并且加载你的第一个"hello world"模块有时就会比较困难。但是当你跨过这道坎时,后面会顺利的多。 1.2.1.1. 内核模块和内核的版本问题 为某个版本编译的模块将不能被另一个版本的内核加载如果内核中打开了CONFIG_MODVERSIONS 选项。我们暂时不会讨论与此相关的内容。在我们进入相关内容前,本文档中的范例可能在该选项打开的情况下无法工作。但是,目前绝大多数的发行版是将该选项打开的。所以如果你遇到和版本相关的错误时,最好,重新编译一个关闭该选项的内核。 1.2.1.2. 使用 X带来的问题 强烈建议你在控制台下输入文档中的范例代码,编译然后加载模块,而不是在X下。模块不能像 printf() 那样输出到屏幕,但它们可以记录信息和警告,当且仅当你在使用控制台时这些信息才能最终显示在屏幕上。 如果你从xterm中insmod一个模块,这些日志信息只会记录在你的日志文件中。除了查看日志文件你将无法 得到输出信息。想要及时的获得这些日志信息,建议所有的工作都在控制台下进行。 1.2.1.3. 编译相关和内核版本相关的问题 Linux的发行版经常给内核打一些非标准的补丁,这种情况回导致一些问题的发生。一个更普遍的问题是一些Linux发行版提供的头文件不完整。编译模块时你将需要非常多的 内核头文件。墨菲法则之一就是那些缺少的头文件恰恰是你最需要的。我强烈建议从Linux镜像站点下载源代码包,编译新内核并用新内核启动系统来避免以上的问题。参阅"Linux Kernel HOWTO"获得详细内容。具有讽刺意味的是,这也会导致一些问题。gcc倾向于在缺省的内核源文件路径(通常是 /usr/src/)。 这可以通过gcc的-I 选项来切换。 注意:
[1] 在早期的linux版本中,是一个名为kerneld的守护进程。
[2] 如果你在修改内核,为避免覆盖你现在工作的模块,你应该试试使用内核Makefile中的 EXTRAVERSION 变量去建立一个独立的模块目 录。
现在,你是不是想编写内核模块。你应该懂得C语言,写过一些用户程序,那么现在你将要见识一些真实的东西。在这里,你会看到一个野蛮的指针是如何毁掉你的文件系统的,一次内核崩溃意味着重启动。 什么是内核模块?内核模块是一些可以让操作系统内核在需要时载入和执行的代码,这同样意味着它可以在不需要时有操作系统卸载。它们扩展了操作系统内核的功能却不需要重新启动系统。举例子来说,其中一种内核模块时设备驱动程序模块,它们用来让操作系统正确识别,使用安装在系统上的硬件设备。如果没有内核模块,我们不得不一次又一次重新编译生成单内核操作系统的内核镜像来加入新的功能。这还意味着一个臃肿的内核。 1.2. 内核模块是如何被调入内核工作的? 你可以通过执行 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已经知道在绝大多数系统上安装的标准的设备的驱动模块。 现在你已经知道内核模块是如何被调入的了。当你想写你自己的依赖于其它模块的内核模块时,还有一些内容没有提供。这个相对高级的问题将在以后的章节中介绍,当我们已经完成前面的学习后。 1.2.1. 在开始前 在我们介绍源代码前,有一些事需要注意。系统彼此之间的不同会导致许多困难。顺利的编译并且加载你的第一个"hello world"模块有时就会比较困难。但是当你跨过这道坎时,后面会顺利的多。 1.2.1.1. 内核模块和内核的版本问题 为某个版本编译的模块将不能被另一个版本的内核加载如果内核中打开了CONFIG_MODVERSIONS 选项。我们暂时不会讨论与此相关的内容。在我们进入相关内容前,本文档中的范例可能在该选项打开的情况下无法工作。但是,目前绝大多数的发行版是将该选项打开的。所以如果你遇到和版本相关的错误时,最好,重新编译一个关闭该选项的内核。 1.2.1.2. 使用 X带来的问题 强烈建议你在控制台下输入文档中的范例代码,编译然后加载模块,而不是在X下。模块不能像 printf() 那样输出到屏幕,但它们可以记录信息和警告,当且仅当你在使用控制台时这些信息才能最终显示在屏幕上。 如果你从xterm中insmod一个模块,这些日志信息只会记录在你的日志文件中。除了查看日志文件你将无法 得到输出信息。想要及时的获得这些日志信息,建议所有的工作都在控制台下进行。 1.2.1.3. 编译相关和内核版本相关的问题 Linux的发行版经常给内核打一些非标准的补丁,这种情况回导致一些问题的发生。一个更普遍的问题是一些Linux发行版提供的头文件不完整。编译模块时你将需要非常多的 内核头文件。墨菲法则之一就是那些缺少的头文件恰恰是你最需要的。我强烈建议从Linux镜像站点下载源代码包,编译新内核并用新内核启动系统来避免以上的问题。参阅"Linux Kernel HOWTO"获得详细内容。具有讽刺意味的是,这也会导致一些问题。gcc倾向于在缺省的内核源文件路径(通常是 /usr/src/)。 这可以通过gcc的-I 选项来切换。 注意:
[1] 在早期的linux版本中,是一个名为kerneld的守护进程。
[2] 如果你在修改内核,为避免覆盖你现在工作的模块,你应该试试使用内核Makefile中的 EXTRAVERSION 变量去建立一个独立的模块目 录。
相关阅读 更多 +