您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 能源与动力工程 > 编译linux外部驱动模块时的基础知识
编译linux外部驱动模块时的基础知识linux内核模块编译引言为了清晰的编译Linux内核,内核编译系统使用Kbuild规则对编译的过程以及依赖进行规约。在内核模块的编译中,为了保持与内核源码的兼容以及传递编译链接选项给GCC,也使用Kbuild规则。内核模块的源代码可以在内核源码树中,也可以在内核源码树外,当使用Kbuild时,两种情况的编译方式也大致相似。一般的内核模块在开发时,都是放在源码树外的。本文主要是针对源码树外部的内核模块的编译。为了屏蔽内核模块编译的复杂性,开发人员需要编写额外的Makefile,最终让编译内核模块就像编译普通的应用程序一样,敲入”make”就行了。本文后面就给了一个实例。编译外部模块在编译外部模块之前,需要首先准备好当前内核的配置以及内核头文件,同时,当前内核的modulesenable选项应该开启(编译内核时指定)。命令行选项使用如下命令编译外部模块:make–Ckernerl_src_dirM=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编译外部模块,默认目标就是modulesmodules_install安装编译成功了的外部模块,默认的安装目录为/lib/modules/kernel_release/extra/,前缀可以同过INSTALL_MOD_PATH指定。clean清除选项help列出可用的外部目标Kbuild文件在执行了make–C/lib/modules/`uname-r`/buildM=`pwd`之后,内核源码树中的Makefile会再次跳转到`pwd`目录下,加载Kbuild或Makefile(如果没有Kbuild文件,则加载Makefile,因此,Kbuild文件中的内容也可以放到Makefile中)。如果模块源码目录中的Kbuild或Makefile中没有定义编译目标时,编译过程最终是什么都没生成的。以下一行就是定义生成目标:obj-m:=module_name1.omodule_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.osrc2.o….Makefile与Kbuild合并为了屏蔽编译内核模块的复杂性,让使用人员简单的调用make/makeinstall即可完成内核模块的编译,模块源码目录下通常添加了一个wrapperMakefile,供向的Makefile包含了Kbuild部分,内容如下:ifneq($(KERNELRELEASE),)obj-m:=hello.oelsedefault::$(MAKE)-C/lib/modules/`uname-r`/buildM=`pwd`modulesendifKbuild与Makefile分离当内核模块源码目录下同时包含了Kbuild与Makefile时,编译系统只加载Kbuild文件。两个文件内容分别如下:Makefile内容如下:default::$(MAKE)-C/lib/modules/`uname-r`/buildM=`pwd`这里的Makefile只是对内核Makefile调用进行了封装。Kbuild内容如下:EXTRA_CFLAGS:=-I.-include./xxx.hobj-m:=module1.omodule2.omodule1-objs:=src1.omodule2-objs:=src2.o头文件在内核源码树中,头文件的存放规则如下:1.如果该头文件定义的是模块内部的接口,则头文件放在模块所在的目录下2.如果头文件中内容在内核其他子系统中使用,则放在include/linux模块版本模块版本选项是通过内核编译选项CONFIG_MODVERSIONS定义的,它是一个简单的ABI兼容性检查机制。对于模块的每个导出符号,都有一个对应的CRC校验值。当模块加载或使用时,内核会用自己的CRC值与模块的CRC值进行对比,如果不同,则拒绝加载模块。在内核源码树根目录中,其中的Module.symvers文件就包含了内核所有的导出符号以及所有编译后模块的导出符号。symbolfromkernel(vmlinux+allmodules)在编译内核时,根目录下会生成Module.symvers文件,它包含了内核以及编译后的模块导出的所有符号。对于每一个符号,相应的CRC校验值也被保存,Module.symvers每一行数据格式如下:CRCSymbolmodule0x2d036834scsi_remove_hostdrivers/scsi/scsi_mod当内核编译选项CONFIG_MODVERSIONS关闭时,所有的CRC值都为0x00000000。Module.symvers文件主要有以下用途:1.列出vmlinux和所有模块的导出函数2.列出所有符号的CRC校验值symbolandexternmodules当编译外部模块时,在MODPOST阶段时,会访问内核源码树中的Module.symvers检测当前模块的外部符号是否已经被定义,同时,如果外部模块源码根目录下包含了Module.symvers文件,该文件也会被检测。对于当前模块的每个外部符号,编译系统都会从当前目录下的Module.symvers以及内核源码树下的Module.symvers中查找,检测是否有该符号。在MODPOST阶段,会在当前目录下生成一个新的Module.symvers,它包含kernel中未定义的所有符号。当外部模块需要从另一个外部模块中导入符号时,有三种方法可以解决。1.使用top-levelKbuild文件例如,两个模块foo.ko,bar.ko,其中foo.ko依赖bar.ko中的导出符号,可以使用一个顶层公用的Kbuild文件对两个模块同时编译,假设目录如下:./foo/=containsfoo.ko./bar/=containsbar.ko顶层的Kbuild文件内容如下:obj-y:=foo/bar/执行make-C$KDIRM=$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.oext2-y:=balloc.obitmap.odir.oext2-$(CONFIG_EXT2_FS_XATTR)+=xattr.o通常情况下,$(CONFIG_EXT2_FS)的值为m,y或未定义。为m时,说明目标模块编译成内核模块,若未y,则目标编译进vmlinux,若未定义,则目标不编译。
本文标题:编译linux外部驱动模块时的基础知识
链接地址:https://www.777doc.com/doc-2068834 .html