Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3034353
  • 博文数量: 167
  • 博客积分: 613
  • 博客等级: 中士
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-13 21:35
个人简介

人, 既无虎狼之爪牙,亦无狮象之力量,却能擒狼缚虎,驯狮猎象,无他,唯智慧耳。

文章分类
文章存档

2015年(19)

2014年(70)

2013年(54)

2012年(14)

2011年(10)

分类: LINUX

2015-09-13 13:26:08

2015/9/13 12:24:31

在进行内核相关的开发时,经常需要根据目标内核在开发平台中建立相应的内核树,那么什么是内核树?为什么需要内核树呢?

一、内核树

简单来说,内核树,就是关于内核中各个目标模块提供的内核API的一个逻辑树;这棵逻辑树对于设备驱动程序或其他内核模块等内核层次的编程来说,是至关重要的。举个相近的例子,我们使用C开发应用程序经常需要包含#include头文件,这些头文件会告诉编译器我们代码中的exit()Printf()等函数在C库中的声明和具体实现,从而在编译成二进制指令时系统可以链接到相应的C函数实现代码,从而生成一个与平台无关的二进制可执行代码。所以对于内核层次的应用开发而言,建立对应版本的内核源码树(简称内核树)是至关重要的,也是首要的一步。

二、下载内核源码

由于Linux不同版本的内核所提供的API可能会有差异,因此我们在编写设备驱动程序或内核模块之前,首先要明确代码运行的内核版本。我们可以使用命令来查看自己的内核版本,即:

uname -r

如果你使用的是CentOS,环境路径中的变量可能会包含现有内核源码的位置,所以也许你不用下载内核源码,但是对于Ubuntu和大多数发行版而言,默认是没有我们需要的内核源码的,因此我们还是需要下载。方法也很简单,使用命令查看官方提供的内核版本,选择安装即可:

opt-cache search linux-source //查看可用内核包

sudo apt-get install linux-source-3.2.0//下载对应的内核版本源码,这里是我的ubuntu-12.04

完成后在/usr/src下会得到lunx-source-3.2.0.tar.bz2,然后使用tar -jxvf命令解压缩得到源码目录linux-source-3.2.0

三、配置内核

一般来说,我们可以使用默认的方式编译内核,即使用命令

sudo make oldconfig

四、编译内核

配置完成后,我们需要运行make 命令进行编译,在2.6之后的内核,命令make make bzImage(生成vmlinux)合二为一,只需要使用第一个即可,这是生成内核文件的关键一步,时间比较长,根据机器配置情况不同,大概需要1-1.5小时左右。

对于3.2.0内核源码编译时遇到了编译错误的问题,即:

MODPOST 3070 modules ERROR: "modverversionshow" [drivers/staging/rts5139/rts5139.ko] undefined! WARNING: modpost: Found 4 section mismatch(es). To see full details build your kernel with: 'make CONFIGDEBUGSECTIONMISMATCH=y' make[2]: *** [_modpost] 错误 1 make[1]: *** [modules] 错误 2

这个问题是因为引入了一个新的函数,但是定义不完善造成的,我们只需要设置不适用staging配置项即可,为了方便我们可以使用内核编译图形工具:

sudo make menuconfig

这个需要提前安装一个包,根据提示使用apt-get install安装即可;

面对图形菜单我感觉找起来太麻烦,直接去修改对应的.config文件,找到其中的RTS5139配置项,将Y改为"N"即可:

make编译完成之后,继续运行命令在/lib/module/下生成3.2.0-90-genetic目录,我们需要下面的build目录进行编译链接。

所有完成后,reboot重启系统。

五、内核模块测试

接下来我们来编写一个最简单的helloworld内核模块,没有具体的功能,仅仅在加载模块和卸载模块时打印输入到syslog中,具体如下:

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
    printk(KERN_ALERT "Hello, world\n");
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "Goodbye, cruel world\n");


module_init(hello_init);
module_exit(hello_exit); 
  1. hello_init() 函数用于初始化内核模块加载时的行为,printk 函数同printf 使用基本相同,不同的是它是内核提供的打印函数,不需要加载C库运行,在内核编程中我们只会使用printk
  2. hello_exit()函数是用于模块卸载时的行为定义;
  3. moduleinit()和moduleexit()两个宏为指定模块初始化和卸载函数,如果没有,对应的处理函数不被调用;

在编写完成hello.c源文件后,我们必须编写Makefile文件,提供make时的配置信息(当然也可以使用命令替代Makefile文件),这里我们的Makefile文件主要包含下面几行:

 1 obj-m := hello.o //告诉内核,由hello.c的目标文件hello.o生成对应的内核模块hello.ko
  2 
  3 KERNELDIR := /lib/modules/3.2.0-90-generic-pae/build  //告诉内核,当遇到hello.c中的内核调用名称时,到下面的这个build目录下查找内核源码树
  4 PWD := $(shell pwd)  //设置shell变量,打印当前目录,使得系统链接完内核树后,回到当前的位置生成module
  5 modules:
  6 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  //实际的make命令,生成hello.ko模块 

最后,我们使用命令:

sudo insmod hello.ko

***sudo lsmod ***

sudo rmmod hello

来安装、查看、卸载相应的内核模块:

六、小结

内核源码树的构建并不复杂, 关键是下载安装正确的内核版本源码,然后配置好成功编译,这其中可能会有些编译的问题。一旦成功获得build目录,即可作为内核模块或者设备驱动程序链接的目录位置,开发相应的内核应用程序。

阅读(8543) | 评论(0) | 转发(8) |
给主人留下些什么吧!~~