📄 x45.html
字号:
<HTML
><HEAD
><TITLE
>内核模块是如何调入内核工作的?</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="The Linux Kernel Module Programming Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Introduction"
HREF="c39.html"><LINK
REL="PREVIOUS"
TITLE="What Is A Kernel Module?"
HREF="x41.html"><LINK
REL="NEXT"
TITLE="Hello World"
HREF="c143.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Linux内核驱动模块编程指南 (内核版本2.2, 2.4)</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="x41.html"
ACCESSKEY="P"
>返回</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>第一章:介绍</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="c143.html"
ACCESSKEY="N"
>继续</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="AEN45"
></A
>1.2. 内核模块是如何被调入内核工作的?</H1
><P
>你可以通过执行 <B
CLASS="COMMAND"
>lsmod</B
>命令来查看内核已经加载了哪些内核模块, 该命令通过读取<TT
CLASS="FILENAME"
>/proc/modules</TT
>文件的内容来获得所需信息。</P
><P
>这些内核模块是如何被调入内核的?当操作系统内核需要的扩展功能不存在时,内核模块管理守护进程kmod<A
NAME="AEN63"
HREF="#FTN.AEN63"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
> 执行modprobe 去加载内核模块。两种类型的参数被传递给modprobe:</P
><P
></P
><UL
><LI
><P
>一个内核模块的名字像<TT
CLASS="FILENAME"
>softdog</TT
>或是<TT
CLASS="FILENAME"
>ppp</TT
>.</P
></LI
><LI
><P
>通用识别符像<TT
CLASS="VARNAME"
>char-major-10-30</TT
>.</P
></LI
></UL
><P
>当传递给modprobe是通用识别符时,modprobe首先在文件 <TT
CLASS="FILENAME"
>/etc/modules.conf</TT
>查找该字符串。如果它发现的一行别名像:</P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
> alias char-major-10-30 softdog
</PRE
></FONT
></TD
></TR
></TABLE
><P
>它就明白通用识别符是指向内核模块<TT
CLASS="FILENAME"
>softdog.o</TT
>.</P
><P
>然后,modprobe遍历文件<TT
CLASS="FILENAME"
>/lib/modules/version/modules.dep</TT
>来判断是否有其它内核模块需要在该模块加载前被加载。该文件是由命令<B
CLASS="COMMAND"
>depmod -a</B
> 建立,保存着内核模块的依赖关系。举例来说,<TT
CLASS="FILENAME"
>msdos.o</TT
> 需要 <TT
CLASS="FILENAME"
>fat.o</TT
> 内核模块已经被内核载入。当要加载的内核模块需要使用别的模块提供的符号链接时(多半是变量或函数),那么那些提供这些所需符号链接的内核模块就被该模块所依赖。</P
><P
>最终,modprobe调用insmod先加载被依赖的模块,然后加载该被内核要求的模块。modprobe将insmod指向 <TT
CLASS="FILENAME"
>/lib/modules/version/</TT
><A
NAME="AEN85"
HREF="#FTN.AEN85"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>目录,该目录为默认标准存放内核模块的目录。insmod对内核模块存放位置的处理相当呆板,所以modprobe应该很清楚的知道默认标准的内核模块存放的位置。所以,当你想要载入一个内核模块时,你可以执行:</P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
> insmod /lib/modules/2.5.1/kernel/fs/fat/fat.o
insmod /lib/modules/2.5.1/kernel/fs/msdos/msdos.o
</PRE
></FONT
></TD
></TR
></TABLE
><P
>或只是执行 "<B
CLASS="COMMAND"
>modprobe -a msdos</B
>"。</P
><P
>Linux提供modprobe, insmod and depmod在一个名为modutils 或 mod-utils的工具包内。</P
><P
>在结束本章前,让我们来看一个<TT
CLASS="FILENAME"
>/etc/modules.conf</TT
>文件:</P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
> #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
</PRE
></FONT
></TD
></TR
></TABLE
><P
>用'#'起始的行为注释。空白行被忽略。</P
><P
>以 <TT
CLASS="LITERAL"
>path[misc]</TT
> 起始的行告诉modprobe用
<TT
CLASS="FILENAME"
>/lib/modules/2.4.?/local</TT
>替代搜寻misc内核模块的路径。正如你看到的,命令解释器shell的元字符也可以使用。</P
><P
>以<TT
CLASS="LITERAL"
>path[net]</TT
> 起始的行告诉modprobe 在目录<TT
CLASS="FILENAME"
>~p/mymodules</TT
>搜索网络方面的内核模块。但是,在<TT
CLASS="LITERAL"
>path[net]</TT
> 指令之前使用的"keep" 指令告诉modprobe只是将该路径添加到标准搜索路径中,而不是像对待misc前面那样进行替换。</P
><P
>以<TT
CLASS="LITERAL"
>alias</TT>
起始的的行使modprobe加载<TT
CLASS="FILENAME"
>eepro.o</TT
> 当kmod 以通用识别符'eth0'要求加载相应内核模块时。</P
><P
>你不会发现像"alias block-major-2 floppy"这样的别名行在文件<TT
CLASS="FILENAME"
>/etc/modules.conf</TT
> 因为modprobe已经知道在绝大多数系统上安装的标准的设备的驱动模块。</P
><P
>现在你已经知道内核模块是如何被调入的了。当你想写你自己的依赖于其它模块的内核模块时,还有一些内容没有提供。这个相对高级的问题将在以后的章节中介绍,当我们已经完成前面的学习后。</P
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AEN123"
></A
>1.2.1. 在开始前</H2
><P
>在我们介绍源代码前,有一些事需要注意。系统彼此之间的不同会导致许多困难。顺利的编译并且加载你的第一个"hello world"模块有时就会比较困难。但是当你跨过这道坎时,后面会顺利的多。</P
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="AEN126"
></A
>1.2.1.1. 内核模块和内核的版本问题</H3
><P
>为某个版本编译的模块将不能被另一个版本的内核加载如果内核中打开了<TT
CLASS="LITERAL"
>CONFIG_MODVERSIONS</TT
> 选项。我们暂时不会讨论与此相关的内容。
在我们进入相关内容前,本文档中的范例可能在该选项打开的情况下无法工作。
但是,目前绝大多数的发行版是将该选项打开的。所以如果你遇到和版本相关的错误时,
最好,重新编译一个关闭该选项的内核。</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="USINGX"
></A
>1.2.1.2. 使用 X带来的问题</H3
><P
>强烈建议你在控制台下输入文档中的范例代码,编译然后加载模块,而不是在X下。
</P
><P
>模块不能像 <TT
CLASS="FUNCTION"
>printf()</TT
> 那样输出到屏幕,但它们可以记录信息和警告,当且仅当你在使用控制台时这些信息才能最终显示在屏幕上。
如果你从xterm中insmod一个模块,这些日志信息只会记录在你的日志文件中。除了查看日志文件你将无法
得到输出信息。想要及时的获得这些日志信息,建议所有的工作都在控制台下进行。</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="AEN135"
></A
>1.2.1.3. 编译相关和内核版本相关的问题</H3
><P
>Linux的发行版经常给内核打一些非标准的补丁,这种情况回导致一些问题的发生。
</P
><P
>一个更普遍的问题是一些Linux发行版提供的头文件不完整。编译模块时你将需要非常多的
内核头文件。墨菲法则之一就是那些缺少的头文件恰恰是你最需要的。
</P
><P
>我强烈建议从Linux镜像站点下载源代码包,编译新内核并用新内核启动系统来避免以上的问题。
参阅"Linux Kernel HOWTO"获得详细内容。</P
><P
>具有讽刺意味的是,这也会导致一些问题。gcc倾向于在缺省的内核源文件路径(通常是 <TT
CLASS="FILENAME"
>/usr/src/</TT
>)。 这可以通过gcc的<TT
CLASS="LITERAL"
>-I</TT
> 选项来切换。</P
></DIV
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>注意</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN63"
HREF="x45.html#AEN63"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>在早期的linux版本中,是一个名为kerneld的守护进程。</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN85"
HREF="x45.html#AEN85"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>如果你在修改内核,为避免覆盖你现在工作的模块,你应该试试使用内核Makefile中的 <TT
CLASS="VARNAME"
>EXTRAVERSION</TT
> 变量去建立一个独立的模块目录。</P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x41.html"
ACCESSKEY="P"
>返回</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>回到首页</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="c143.html"
ACCESSKEY="N"
>继续</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>什么是内核模块?</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="c39.html"
ACCESSKEY="U"
>回到本章开始</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Hello World</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -