Bootstrap

linux内核编译基础知识,Linux内核模块的编译基础知识

关于

1 内核模块的概念

介绍内核模块的同时,也说明一下和应用程序的区别。虽然内容很多,但觉得都很有用。

1、内核模块是一些可以让操作系统内核在需要时载入和执行的代码,同时在不需要的时候可以卸载。这是一个好的功能,扩展了操作系统的内核功能,却不需要重新启动系统,是一种动态加载的技术。

特点:动态加载,随时载入,随时卸载,扩展功能

2、内核模块的加载作用:只是向linux内核预先注册自己,以便于将来的请求使用。

也就是告诉内核,它有了新增的功能,而并不马上使用(执行),而应用程序在加载后就开始执行。

3、内核模块的代码编写没有外部的函数库可以用,只能使用内核导出的函数。而应用程序习惯于使用外部的库函数,在编译时将程序与库函数链接在一起。例如对比printf( ) and printk( )

4、内核模块代码运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。

最少两个入口点

*模块加载函数

*模块卸载函数

通常使用module_init() and module_exit()两个宏定义声明模块的加载函数和卸载函数,这个定义在linux2.6.x/include/linux/init.h中。内容为:

#define module_init(x) __initcall(x)

//在内核启动或模块加载时执行

#define module_exit(x) __exitcall(x)

//在模块卸载时执行

*每一个模块只能有一个module_init 和一个module_exit。

一个简单的内核模块的源代码.c格式如下:

-------------------------------------------------------------------------------------------

#include   //所有内核模块都必须包含这个头文件

#include    //使用内核信息优先级时要包含这个

#include         //一些初始化的函数如module_init()

MODULE_LICENSE('GPL');  //模块许可声明

static int hello_init(void)

{}

static void hello_exit(void)

{}

module_init(hello_init);

module_exit(hell0_exit);

这两个函数只是一种内部的声明,或者说内部实现加载的一种方式,说到低还是在模块内部的,内核并没有加载模块,真正要加载模块要使用命令insmod卸载用rmmod.

关于linux设备驱动入门请参考另外一篇文章:

--------------------------------------------------------------------------------

3    2.6 系列内核模块的编译和加载

这里讲到内核模块的编译,2。6版本引入了kbuild,将外部内核模块的编译和内核源码树的编译统一起来了。

内核源码树的目录下都有两个文件Kconfig(2.4版本是Config.in)和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文件相关的内核配置菜单。在内核配置make menuconfig(或xconfig等)时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文件中。在内核编译时,主 Makefile调用这个.config,就知道了用户的选择。

*上面的内容说明了,Kconfig就是对应着内核的配置菜单。如果要想添加新的驱动到内核的源码中,可以修改Kconfig,这样就可以选择这个驱动,如果想使这个驱动被编译,要修改Makefile

添加新的驱动时需要修改的文件有两种(注意不只是两个)

*Kconfig

*Makefile

要想知道怎么修改这两种文件,就要知道两种文件的语法结构

3.1 Kconfig

每个菜单都有一个关键字标识,最常见的就是config

语法:

config

symbol是一个新的标记的菜单项,options是在这个新的菜单项下的属性和选项

其中options部分有:

1、类型定义:

每个config菜单项都要有类型定义,bool布尔类型、 tristate三态:内建、模块、移除 string字符串、 hex十六进制、 integer整型

例如config HELLO_MODULE

bool 'hello test module'

bool类型的只能选中或不选中,tristate类型的菜单项多了编译成内核模块的选项,如果选择编译成内核模块,则会在.config中生成一个CONFIG_HELLO_MODULE=m的配置,如果选择内建,就是直接编译成内核影响,就会在.config中生成一个 CONFIG_HELLO_MODULE=y的配置.

2、依赖型定义depends on或requires

指此菜单的出现与否依赖于另一个定义

config HELLO_MODULE

bool 'hello test module'

depends on ARCH_PXA

这个例子表明HELLO_MODULE这个菜单项只对XScale处理器有效。

3、帮助性定义

只是增加帮助用关键字help或者---help---

3.2 内核的Makefile

在linux2.6.x/Documentation/kbuild目录下有详细的介绍有关kernel makefile的知识。

linux2.6内核的Makefile分为5个组成部分:

Makefile     最顶层的Makefile

.config        内核的当前配置文件,编译时成为定层Makefile的一部分

arch/$(ARCH)/Makefile    与体系结构相关的Makefile

s/ Makefile.*      一些Makefile的通用规则

kbuild Makefile           各级目录下的大概约500个文件,编译时根据上层Makefile传下来的宏定义和其他编译规则,将源代码编译成模块或者编入内核

顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 s目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。

(其中.config的内容是在make menuconfig的时候,通过Kconfig文件配置的结果,上面已经说过)

对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。主要部分有:1、目标定义,目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。

最简单的只有一行,如

obj-y = foo.o

表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。除了y,m以外的obj-x形式的目标都不会被编译。

由于既可以编译成模块,也可以编译进内核,更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式,如:

obj-$(CONFIG_HELLO_MODULE) = hello.o ,这个已经在7.2.3.1中说明过。

除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下

2、多目标

一个内核模块由多个源文件编译而成,这是Makefile有所不同。

采用模块名加 –objs后缀或者 –y后缀的形式来定义模块的组成文件。

如以下例子:

obj-$(CONFIG_EXT2_FS) = ext2.o

ext2-y := balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR) = xattr.o

模 块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o 直至ext2.ko文件,是否包括xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的 ext2.o将被链接进built-in.o最终链接进内核。这里需要注意的一点是,该kbuild Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s

或者写成如-objs的形式:

obj-$(CONFIG_ISDN) = isdn.o

isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

3、目录的迭代

obj-$(CONFIG_EXT2_FS) = ext2/

如果CONFIG_EXT2_FS 的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的

4、不同的模块编译方式

编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:

make -C path/to/kernel/src M=$PWD modules

-C指定内核源码的根目录,$PWD 或 `PWD` 是当前目录的环境变量,告诉kbuild回到当前目录来执行build操作。

5、模块安装

当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH 指定一个前缀,如:

make INSTALL_MOD_PATH=/foo modules_install

模块将被安装到 /foo/lib/modules目录下

注意2.6和2.4版本编译结果的不同,内核模块在2.6下是.ko后缀,取代了2.4下的.o后缀,使内核模块和普通的目标文件区别开。(比较好呀)

;