写在前面
作者一直支持GPL的精神。允许任何人*使用、转载、复制和再分发,但必须保留作者署名,必须保证全文完整转载,包括完整的版权声明。
由于作者水平有限,因此不能保证文章内容准确无误,请批判阅读。如果你发现任何错误或对文章内容有任何建议,欢迎你与我联系:
Email: hunaiquan@126.com
为什么要构建内核树
在《LDD3》的第二章P21,有以下一段话:
“不管内核来自哪里,想要为2.6.x内核构造模块,还必须在自己的系统中配置并构造好内核树。这一要求和先前版本的内核不同,先前的内核只要有一套内核头文件 就够了。但因为2.6内核的模块要和内核源代码树中的文件连接,通过这种方式,可得到一个更加健壮的模块装载器,但也需要这些目标文件存在于内核目录树 中。这样,读者首先要准备好一个内核源代码树。”
这段话说出了2.4和2.6两种版本的驱动模块的编写的一个不同之处。我使用到是ubnutu,所以下面我将以ubuntu为例来构建内核树。如果你安装到是ubuntu 10.04或者是11.04的话,默认已经安装来内核树,可以到 /lib/modules/$(uname -r)/build查看 。如果没有,就需要重新构建。
1.安装编译内核所需要的软件
$sudo apt-get install build-essential kernel-package libncurses5-devlibncurses5这个软件包在使用menuconfig配置内核的时候会用到。
2.下载内核源码
先查看linux内核版本:$uname -r
然后用apt-cache search linux-source命令, 会列出一些可选源码包,对准你的内核版本号,选择“with Ubuntu patche”的那个,最后用apt-get install linux-source-2.6.38.8下载之(或者直接去www.kernel.org下载)。解压缩源码包:
[eric@eric-Computer:src$] sudo tar jxvf linux-source-2.6.38.tar.bz2
进入解压后的源码目录:
[eric@eric-Computer:src$] cd linux-source-2.6.38/
3.配置内核
在编译之前我们需要Ubuntu原来内核的一个配置文件,这是我/usr/src目录下的文件预览:ls -al
drwxr-xr-x 4 root root 4096 2010-09-04 21:31 fglrx-8.723.1
drwxr-xr-x 24 root root 4096 2010-09-04 20:35 linux-headers-2.6.38-8
drwxr-xr-x 7 root root 4096 2010-09-04 20:35 linux-headers-2.6.38-8-generic
drwxr-xr-x 25 root root 4096 2010-09-16 21:39 linux-source-2.6.38.8
-rw-r--r-- 1 root root 65846876 2010-09-01 22:41 linux-source-2.6.38-8.tar.bz2
如果你使用原先系统的配置(默认配置),可执行一下步骤:
[eric@eric-Computer:linux-source-2.6.38$] sudo make oldconfig
4.编译内核
接下来我们就要开始编译了。
[eric@eric-Computer:linux-source-2.6.38$] sudo make
(记住一定要是管理员帐号运行,这个过程很久,如果你的cpu是双核的可以在make后面加个参数,make -j4.)
[eric@eric-Computer:linux-source-2.6.38$] sudo make bzImage(注意,I要大写。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。 )
# make install
#make modules (编译 模块 )
#make modules_install
(这条命令能在/lib/modules目录下产生一个目录)
重启电脑,grup menu中就出现了新内核选项
5.使用hello模块测试内核
如果一切顺利,编译过程中不出现什么错误的话,接下来我们就可以编写最简单的linux模块——helloworld了。
在某一目录下创建2个文件:hello.c和Makefile
hello.c 内容如下:
- /*
- * $Id: hello.c,v 1.5 2011/06/21 03:32:21 eric $
- */
- #include <linux/init.h>
- #include <linux/module.h>
- 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);
Makefile内容如下:
- # To build modules outside of the kernel tree, we run "make"
- # in the kernel source tree; the Makefile these then includes this
- # Makefile once again.
- # This conditional selects whether we are being included from the
- # kernel Makefile or not.
- ifeq ($(KERNELRELEASE),)
-
- # Assume the source tree is where the running kernel was built
- # You should set KERNELDIR in the environment if it's elsewhere
- KERNELDIR ?= /lib/modules/$(shell uname -r)/build
- # The current directory is passed to sub-makes as argument
- PWD := $(shell pwd)
-
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-
- modules_install:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
-
- clean:
- rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
-
- .PHONY: modules modules_install clean
-
- else
- # called from kernel build system: just declare what our modules are
- obj-m := hello.o
- endif
需要注意的是Makefile的格式要正确。
然后编译:
$ make
不出现错误的话,用ls -al查看该目录,会产生如下文件:
hello.c hello.mod.c hello.o modules.order
hello.ko hello.mod.o Makefile Module.symvers
现在我们就可以将编译好的模块hello加载到内核中去了
$ sudo insmod ./hello.ko //这个命令把hello.ko加载到内核
$ sudo lsmod|grep hello //lsmod 这个命令可以查看当前所有的驱动模块,结果应该显示hello 680 0
$ sudo rmmod hello //这个命令是把hello这个模块移除掉
程序的输出结果可以在
/var/log/syslog文件中查看
Hello,World
Goodbye,cruel world
这是程序的输出。