编译linux外部驱动模块时的基础知识

原创 2013年12月05日 14:46:21
linux内核模块编译引言

为了清晰的编译Linux内核,内核编译系统使用Kbuild规则对编译的过程以及依赖进行规约。在内核模块的编译中,为了保持与内核源码的兼容以及传递编译链接选项给GCC,也使用Kbuild规则。

内核模块的源代码可以在内核源码树中,也可以在内核源码树外,当使用Kbuild时,两种情况的编译方式也大致相似。一般的内核模块在开发时,都是放在源码树外的。

本文主要是针对源码树外部的内核模块的编译。为了屏蔽内核模块编译的复杂性,开发人员需要编写额外的Makefile,最终让编译内核模块就像编译普通的应用程序一样,敲入”make”就行了。本文后面就给了一个实例。

编译外部模块

在编译外部模块之前,需要首先准备好当前内核的配置以及内核头文件,同时,当前内核的modules enable选项应该开启(编译内核时指定)。

命令行选项

使用如下命令编译外部模块:

make –C <kernerl_src_dir>M=<ext_module_path>

其中-C表明make要调用<kernel_src_dir>下的Makefile,该Makefile就是内核的Makefile,M为该Makefile的参数,指定外部模块源码的路径。当Makefile接收到M参数时,就默认编译外部模块。

例如,当前目录下存放一个外部模块的源码,其编译命令如下:

make –C /lib/modules/`uname -r`/buildM=`pwd`

其中uname –r获取当前运行内核的版本,pwd为当前源码路径,将其展开之后为:

make –C /lib/modules/ 2.6.42.9/buildM=/home/user/hello

其中/lib/modules/ 2.6.42.9/build是指向内核源码目录的符号链接。

编译完成之后,要安装驱动时,调用如下命令:

make –C /lib/modules/`uname -r`/buildM=`pwd` modules_install

编译目标

modules

编译外部模块,默认目标就是modules

modules_install

安装编译成功了的外部模块,默认的安装目录为/lib/modules/<kernel_release>/extra/,前缀可以同过INSTALL_MOD_PATH指定。

clean

清除选项

help

列出可用的外部目标

Kbuild文件

在执行了make –C /lib/modules/`uname-r`/build M=`pwd`之后,内核源码树中的Makefile会再次跳转到`pwd`目录下,加载Kbuild或Makefile(如果没有Kbuild文件,则加载Makefile,因此,Kbuild文件中的内容也可以放到Makefile中)。

如果模块源码目录中的Kbuild或Makefile中没有定义编译目标时,编译过程最终是什么都没生成的。以下一行就是定义生成目标:

obj-m: = <module_name1>.o <module_name2>.o… 

上面的obj-m变量是指外部模块,其后面的一组<module_name?>.o最终生成<module_name?>.ko模块。同样,还有一个变量obj-y,它包含要静态编译进入内核的模块。本文不考虑它。

在默认情况下,内核源码编译系统会将<module_name1>.c编译成<module_name1>.o,并最终链接生成<module_name1>.ko。如果<module_name1>.ko需要多个源文件时,Kbuild或Makefile中要添加如下行:

<module_name1>-y: = src1.o src2.o ….

Makefile与Kbuild合并

为了屏蔽编译内核模块的复杂性,让使用人员简单的调用make/makeinstall即可完成内核模块的编译,模块源码目录下通常添加了一个wrapper Makefile,供向的Makefile包含了Kbuild部分,内容如下:

ifneq ($(KERNELRELEASE),)

obj-m := hello.o 

else

default::

$(MAKE) -C /lib/modules/`uname -r`/buildM=`pwd` modules

endif

Kbuild与Makefile分离

当内核模块源码目录下同时包含了Kbuild与Makefile时,编译系统只加载Kbuild文件。两个文件内容分别如下:

Makefile

内容如下:

default::

       $(MAKE)-C /lib/modules/`uname -r`/build M=`pwd`

这里的Makefile只是对内核Makefile调用进行了封装。

Kbuild

内容如下:

EXTRA_CFLAGS := -I.-include ./xxx.h

obj-m := module1.o module2.o

module1-objs := src1.o

module2-objs := src2.o

头文件

在内核源码树中,头文件的存放规则如下:

1.如果该头文件定义的是模块内部的接口,则头文件放在模块所在的目录下

2.如果头文件中内容在内核其他子系统中使用,则放在include/linux

模块版本

模块版本选项是通过内核编译选项CONFIG_MODVERSIONS定义的,它是一个简单的ABI兼容性检查机制。对于模块的每个导出符号,都有一个对应的CRC校验值。当模块加载或使用时,内核会用自己的CRC值与模块的CRC值进行对比,如果不同,则拒绝加载模块。

在内核源码树根目录中,其中的Module.symvers文件就包含了内核所有的导出符号以及所有编译后模块的导出符号

symbol from kernel(vmlinux+all modules)

在编译内核时,根目录下会生成Module.symvers文件,它包含了内核以及编译后的模块导出的所有符号。对于每一个符号,相应的CRC校验值也被保存,Module.symvers每一行数据格式如下:

<CRC>                 <Symbol>                    <module>

0x2d036834        scsi_remove_host          drivers/scsi/scsi_mod

当内核编译选项CONFIG_MODVERSIONS关闭时,所有的CRC值都为0x00000000。

Module.symvers文件主要有以下用途:

1.列出vmlinux和所有模块的导出函数

2.列出所有符号的CRC校验值

symbol and extern modules

当编译外部模块时,在MODPOST阶段时,会访问内核源码树中的Module.symvers检测当前模块的外部符号是否已经被定义,同时,如果外部模块源码根目录下包含了Module.symvers文件,该文件也会被检测。

对于当前模块的每个外部符号,编译系统都会从当前目录下的Module.symvers以及内核源码树下的Module.symvers中查找,检测是否有该符号。

在MODPOST阶段,会在当前目录下生成一个新的Module.symvers,它包含kernel中未定义的所有符号。

当外部模块需要从另一个外部模块中导入符号时,有三种方法可以解决。

1.使用top-level Kbuild文件

例如,两个模块foo.ko,bar.ko,其中foo.ko依赖bar.ko中的导出符号,可以使用一个顶层公用的Kbuild文件对两个模块同时编译,假设目录如下:

./foo/ <= contains foo.ko

./bar/ <= contains bar.ko

顶层的Kbuild文件内容如下:

obj-y := foo/  bar/

执行make -C $KDIR M=$PWD,在编译过程中,两个模块的导出符号是共享的。

2.使用额外的Module.symvers文件

首先生成外部模块bar.ko,生成之后,bar.ko目录下生成一个Module.symvers文件,它包含了kernel中的Module.symvers未定义的所有符号(当然包含bar.ko的导出符号)。

在编译foo.ko时,为了能访问bar.ko的导出符号,可以将bar.ko生成的Module.symvers文件复制到foo编译目录。编译时,会读取foo目录下的Module.symvers文件,同时,生成一个所有未在kernel中定义的符号文件Module.symvers。

3. 调用make时传入参数KBUILD_EXTRA_SYMBOLS

 

杂项

模块编译有时候需要通过检查内核编译选项CONFIG_option决定哪些功能被编译进模块,在Kbuild中,可以直接使用这些选项。

例如:

obj-$(CONFIG_EXT2_FS) += ext2.o

ext2-y := balloc.o bitmap.o dir.o

ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o

通常情况下,$(CONFIG_EXT2_FS)的值为m,y或未定义。为m时,说明目标模块编译成内核模块,若未y,则目标编译进mlinux,若未定义,则目标不编译。

相关文章推荐

linux驱动模块编译(初学者)

linux 模块编译步骤(转))    本文将直接了当的带你进入linux的模块编译。当然在介绍的过程当中,我也会添加一些必要的注释,以便初学者能够看懂。之所以要写这篇文章,主要是因为从书本上学的话,...

如何写Linux下编译驱动模块的Makefile

编译驱动模块的Makefile的写法示例: #General Purpose Makefile for Linux Kernel module by guoqingbo KERN_DIR = /...
  • lqjun
  • lqjun
  • 2015年10月22日 20:59
  • 1264

一步一步学习 Linux 驱动之驱动模块 MakeFile 解析

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。 一、使用条件判断 下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU...

Makefile选项CFLAGS,LDFLAGS,LIBS

Makefile选项CFLAGS,LDFLAGS,LIBS CFLAGS 表示用于 C 编译器的选项, CXXFLAGS 表示用于 C++ 编译器的选项。 这两个变量实际上涵盖了...

linux驱动入门——模块参数和导出符号

驱动程序中lsmod命令实际读取的是/proc/modules文件  即与lsmod命令对应的结果是cat /proc/modules  内核中已经加载的模块的信息存在于/sys/module...
  • eydwyz
  • eydwyz
  • 2016年12月09日 17:35
  • 253

Linux下编译安装驱动的两种方式

linux 编译安装驱动有两种,动态加载与静态加载 动态加载 一,编译,在指点内核树下编译,生成.o文件或.ko文件 二,将生成的.o或.ko文件拷到相应目录,一般是/lib/module/ke...
  • xxgxgx
  • xxgxgx
  • 2016年02月18日 11:10
  • 2796

modules的Makefile

Makefile前的准备工作: 1、要有一个编译好的内核目录(里面已经配置好CPU架构、交叉工具链)       原因是:modules的Makefile包含一个编译好的内核的目录进来,是要将该m...

Linux内核驱动模块编译

Linux内核模块编译

通过串口连接树莓派ssh

现在网上很多资料是连接树莓派进行串口通讯,这篇文章是用串口来连接树莓派的ssh。 树莓派串口连接官方资料 前期准备 1、USB转串口工具,比如FTDI TTL-232R-RPI 2、杜邦线操作...
  • talkxin
  • talkxin
  • 2016年01月10日 19:45
  • 3920

RC522 模块驱动程序

本文主要讲述了基于SPI总线的RC522驱动程序的设计。描述了主控如何与从设备通过SPI总线进行数据的读写。 一 在SPI驱动中,有两个重要的结构:spi_device&spi_driver。 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:编译linux外部驱动模块时的基础知识
举报原因:
原因补充:

(最多只允许输入30个字)