现在的位置: 首页 > 综合 > 正文

moudle.txt

2013年03月20日 ⁄ 综合 ⁄ 共 6441字 ⁄ 字号 评论关闭

在这个文档里你可以找到以下信息:
-如何建立一个外部模块
-如何用kbuild结构来编译你的模块
-如何在非标准定位处安装你的模块

=== 内容表

    === 1 简介
    === 2 如何建立外部模块
       --- 2.1 建立外部模块
       --- 2.2 可用的目标
       --- 2.3 可用的选项
       --- 2.4 为建立模块准备的内核树
    === 3 示例命令
    === 4 为外部模块建一个 kbuild 文件
    === 5 包含文件
       --- 5.1 如何包含内核include目录中的文件
       --- 5.2 外部模块使用 include/ 目标
    === 6 模块安装
       --- 6.1 INSTALLL_MOD_PATH
       --- 6.2 INSTALL_MOD_DIR
    === 7 模块版本
    === 8 提示和技巧
       --- 8.1 测试 CONFIG_FOO_BAR

=== 1. 简介

kbuild 的功能包括内核源代码树的内部和外部的模块.稍后会经常提及到外部模块,它是用来在开发中使用的,而没有打算把它放到内核树中.

这个文件掩盖了模块作者的主要信息.外部模块的作者必须提供一个Makefile 文件来隐藏复杂的细节,使别人只要输入"make"就可以编译这个模块.一个完整的例子将会在第 4章描述:为外部模块创建一个kbuild文件".

=== 2. 如何构建一个外部模块

kbuild具有利用完整代码预构建的内核变量来构建外部模块的功能.在构建外部模块的时候,一系列在构建内核有效时的目标变得有效.

 --- 2.1 构建外部模块
   
    利用下面的命令来构建一个外部模块:
    make -C <path-to-kernel> M=`pwd`

    如果用正在运行着的内核用:
    make -C /lib/modules/`uname -r`/build M=`pwd`
    如果要使上面的命令成功执行,内核必须是编译成可以模块加载的.

    安装模块可以用以下命令:
    make -C <path-to-kernel> M=`pwd` modules_install

    上面只是个开头,后面有更复杂的例子.

--- 2.2 有效的目标

   $KDIR 是内核源代码的根目录

   make -C $KDIR M=`pwd`
    将在当前目标下创建模块.所有的输出文件都在模块源代码的目录下生成.没有企图去更新内核源代码,同时它是对内核成功执行make的先决条件.

   make -C $KDIR M=`pwd` modules
    当没有指定目标时,模块目标是暗指的.它的功能是和没有指定特定目标是一样的,像上面描述的 样.
   make -C $KDIR M=$PWD modiles_install
    安装外部模块.
    默认安装目录为 /lib/modules/<kernel-version>/extra
    但是可以用INSTALL_MOD_PATH指定安装目录 - 看另外一章

   make -C $KDIR M=$PWD clean
    删除所有构建模块生成的文件 - 没有更改内核源代码的目录.

   make -C $KDIR M=`pwd` help
    列出在构建外部模块可用的目标.

--- 2.3 可用的选项:

    $KDIR 内核源代码路径

    make -C $KDIR
    用来指定内核源代码的路径.
       '$KDIR'描述内核源代码的目录.Make 会进入实际指定的目录执行,但是完成执行时会返回当前的目录.
    make -C $KDOR M=`pwd`
     M= 用来告诉kbuild正在构建一个外部模块.
    M= 是外部模块(kbuild 文件)所在的目录.
    make -C $KDIR SUBDIR=`pwd`
    和M=选项一样,SUBDIRS= 的语法是向后兼容的.

-- 2.4 为构建模块准备内核树
 
    为了确保构建外部模块所必需的内核信息, 必须使用'modules_prepare'目标.
    'modules_prepare' 仅仅为构建外部模块简单地提供一个内核.
   注意: modules_prepare没有构建模块,即使设置了CONFIG_MODULEVERSIONING.
        因些,构建一个可工作版本的模块必须要一个完整的内核build.

=== 3. 命令例子

这个例子显示了在为一个当前运行着内核构建一外部模块实际的命令.
在下面的例子是假设编译内核的输出文件和内核源代码的目录是不相同的,但是这个例子对于模块和内核树混合于不同的目录同样是适用的.

#内核源代码
/lib/modules/<kernel-version>/build -> /usr/src/linux-<version>

#来自内核编译的输出
/lib/modules/<kernel-version/build -> /usr/src/linux-<version>-up

改变到kbuild 文件的所在目录和执行以下的命令来构建模块:

    cd /home/user/src/module
    make -C /usr/src/`uname -r`/source   \
         -O=/lib/modules/`uname -r`/build \
          M= `pwd`     
之后执行以下的命令来安装模块:
    make -C /usr/src/`uname -r`/source  \
         -O=/lib/modules/`uname -r`/build   \
            M= `pwd`                \

如果你仔细看下你就会发现这个像之前的命令是一样的 - 只是目录转写出来而已.

上面的命令都很长,下面的章节将讲一些技巧来使它变得更简单.

=== 4.为外部模块创建一个kbuild文件.

kbuild是内核的编译系统,外部模块要构建系统中必须用kbuild来使改变保持兼容,和为gcc选择合适的标志(flags).

kbuild文件的用于输入的语法格式像/Documention/kbuild/makefiles.txt描述的那样.这个章节将会介绍更多的技巧用来处理外部的模块.

根据下面的文件来创建一个Makefile文件:
    8123_if.c
    8123_if.h
    8123_pci.c
    8123_bin.o_shipped  <=Binary blob

--- 4.1内核与模块之间共享Makefile

    一个外部模块通常包括一个已经封闭好了的Makefile,编译这个模块只要'make'就可以而不必要另加参数. 另外Makefile还提供了更多的功能,像测试目标等等这样的功能.如果这部份的名字冲突影响了kduild,必须把这部分过滤掉.

    例 1:
     --> 文件名: Makefile
    ifneq ($(KERNELRELEASE),)
    #makefile 中kbuild的部分
    obj-m   := 8123.o
    8123-y := 8123_if.o 8123_pci.o 8123_bin.o
    else
    #通用的Makefile形式
    KERNELDIR := /lib/modules/`uname -r`/build
    all:
        $(MAKE) -C $(KERNELDIR) M=`pwd` $@
    # Module specific targets
    genbin:
        echo "X" > 8123_bini.o_shipped
    endif

在例子1里面检查KERNELRELEASE是用来分离Makdefile的两部分的.kbuild只看到前面两个赋值语句,而make即除了这两个语句以外其它的都可以看到.

在近版本的内核,kbuild查找一命名为Kbuild的文件,而命名为Makefile为第二个参数.利用Kbuild
文件可以把例子1中的Makefile分裂成像例子2那样:

     例子 2:
        --> filename: Kbuild
    obj-m  := 8123.o
    8123-y := 8123_if.o 8123_pci.o 8123_bin.o

    --> filename: Makefile
    KERNELDIR := /lib/modules/`uname -r`/build
    all::
        $(MAKE) -C $KERNELDIR M=`pwd` $@

    # Module specific targets
    genbin:
        echo "X" > 8123_bin_shipped

    在例子2中我们相当简单地把文件分裂成两个简单的文件,那是值得怀疑的.但是一些外部模块用几百行的Makefile.在这里真正地从余下的份离出kbuild部分.
例子3表明了向后兼容的版本.

    例子3:
      --> 文件名: Kbuild
    obj-m   := 8123.o
    8123-y := 8123_if.o 8123_pci.o 8123_bin.o

     --> 文件名: Makefile
    ifneq ($(KERNELRELEASE),)
    include Kbuild
    else
    #Normal Makefile
   
    KERNELDIR  := /lib/modules/`uname -r`/build
    all:
        $(MAKE) -C $KERNELDIR M=`pwd` $@

    # Module specific targets
    genbin:
        echo "X" > 8123_bin_shipped
   
    endif

    这里用的技巧是:Makefile里包含了Kbuild文件,当是一个旧的版本kbuild的时候,Kbuild就会被包含进去.

--- 4.2 模块中包含二进blob
     一些外部模块需要包含一些以a.o为后缀的二进制blob.kbuild可以支持这种方法.但是必须要把文件命名成<filename>_shipped.在我们这个例子中,这个blob是8213_bin.o_shipped.当kbuild构建时只要把后缀去掉就可以. 这样就允许了模块中分配了8123_bin.o中的内容.

    例子4:
    obj-m  := 8123.o
    8123-y := 8123_if.o 8123_pci.o 8123_bin.o
   在例子4里的.c/.h源文件与二进制文件没有区别,但是kbuild 可以选择不同的规则来创建.o文件.

=== 5. 包含文件
    当一个.c文件用到另一个.c文件的时候,就要用到包含文件(不是严格意义上的.c文件,但是良好的编程习惯是那样用).任何模块包含多个.c文件都会有一个多个.c文件的.h文件.
-如果.h文件只是描述一个模块的内部接口,那么.h文件必须和.c文件放在相同的目录下.
-如果.h文件描述的接口功能是内核的其它目录,而不是和当前模块在同一个目录时,这个.h文件即放在include/linux/或者其它适当的目录,一般根据其功能来划分.
另外,对于大的子系统即有一个规则,它们在include/下都有自己的包含目录.像 include/scsi.另一个例外即是与特殊架构相关的头文件都在include/asm-$(ARCH)/ *.

对于外部模块即有一个倾向把包含文件放在一个独立的include/目录里,因些在他们的kbuild文件里要对这些作处理.

--- 5.1如何从内核包含目录中包含文件?
    当一个模块要从目录include/linux/下包含一个文件,只要像下面这样:
    #include<linux/modules.h>

   kbuild将确保新增的参数选项给gcc使它查找相关的目录.
    同样,如果.h文件与.c文件放在相同的目录下,即像下面一样:
    #include"8123_if.h"
--- 5.2 外部模块包含目录 include/dir
    外部模块通常把它的.h文件放在单独的include/ 目录.虽然这通常不是内核的风格.当一个外部模块采用这样的包含目录,kbuild必须被告知. 有个技巧就是用EXTRA_CFLAGS(对所有的.c文件都适用)或者 CFLAGS_$F.o(对单个文件适用).

    在我们的例子中,如果我们把8123_if.h文件到一个命名为 include/的子目录.那么 kbuild 文件就像下面一样:

    --> filename : Kbuild
    obj-m   := 8123.o

    EXTRA_CFLAGS   := -Iinclude
    8123-y := 8123_if.o 8123_pci.o 8123_bin.o

      注意,在-I与目录间是没有空格的.这是kbuild里必须限制的且一定不出现空格.

=== 6. 模块安装
    内核模块安装在以下目录:

    /lib/modules/$(KERNELRELEASE)kernel
    外部模块安装在以下目录:
    /lib/modules/$(KERNELRELEASE)extra

--- 6.1 INSTALL_MOD_PATH
    上面说的是默认的目录,但像以往一样,个性化地订制自己的目录也是允许的.我们可以使用安装前缀变量  INSTALL_MOD_PATH:
    $ make INSTALL_MOD_PATH=/frodo modules_install
        => Install dir: /frodo/lib/modules/$(KERNELRELEASE)/kernel
    
    INSTALL_MOD_PATH 像其它shell变量一样,如果在上述例子中指定了这个变量,调用make时,对内核模块和外部模块都是有效力的.

--- 6.2 INSTALL_MOD_DIR
    当安装外部模块时,它们会被默认安装到/lib/modules/$(KERNELRELEASE)/extra,但是如果你想把具体功能的模块放到单独的目录,基于这个目的,你可以用使用 INSTALL_MOD_DIR 参数来指定代替'extra'
   
    $ make INSTALL_MOD_DIR=gandalf -C KERNELDIR \
            M=`pwd` modules_install
    => Install dir: /lib/modules/$(KERNELRELEASE)/gandalf

==== 7. 模块版本

CONFIG_MODVERSIONS 标记启用模块版本.模块版本是用来作简单的ABI一致性检查的. 模块版本为一个导出符号原型创建一个CRC值.当一个模块被加载时,内核中的CRC值与模块中同类值比较,如果不相等,内核即拒绝加载这个模块.

在构建内核的过程中会产生一个名为Module.symvers的文件.这个文件包含了内核中所有的符号版本.如果Module.symvers 文件是最新编译内核时产生的, 即不必再完整地编译内核来建立一个兼容的模块.

=== 8. 提示和技巧

--- 8.1 测试CONFIG_FOO_BAR
 当kbuild是用来直接引用CONFIG_ 变量时,模块往往需要检查某些CONFIG_选项,以决定是否具体特性应包括在模块以内。

        #fs/ext2/Makefile
        obj-$(CONFIG_EXT2_FS) += ext2.o

        ext2-y := balloc.o bitmap.o dir.o
        ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o

外部模块历来用传统的grep办法来直接检查在.config文件中的具体CONFIG_ 设置.这种方法已经被打破了.正如之前介绍的外部模块时,应使用kbuild建立.因此,可以使用相同的方法,在内核模块时测试CONFIG_的定义。

 

抱歉!评论已关闭.