本文共 3790 字,大约阅读时间需要 12 分钟。
个人用的Linux版本为:FC6/Linux,内核版本为:2.6.18-1.2798.fc6 编译工具:gcc-3.4.1
1.获得Linux内核的源代码,即构建LDD3(Linux Device Drivers 3rd)上面所说的内核树。
如果安装的Linux系统中已经自带了源代码的话,应该在/usr/src目录下。如果该
目录为空的话,则需要自己手动下载源代码。下载代码的方法和链接很多,也可以在上通
过http://download.chinaunix.net/search/?key=&q=kernel&frmid=53去下载。不过,下载的内
核版本最好和所运行的Linux系统的内核版本一致。当然,也可以比Linux系统内核的版
本低,但高的话应该不行(个人尚未实践)。
2.下载完成后,安装在/usr/src下,文件名为:2.6.18-1.2798.fc6-i586.tar.bz2,是一个压缩包,解压缩既可以得到整个内核的源代码:
# tar jxvf 2.6.18-1.2798.fc6-i586.tar.bz2
解压后生成一个新的目录/usr/src/2.6.18-1.2798.fc6-i586,所有的源代码都在该目录下。
注:该目录会因内核版本的不同而不同,各位动手实践的朋友只需知道自己的源代码所在的具体位置即可。
3.稍微更改一下Makefile: 每个内核的名字都包含了它的版本号,这也是 uname -r 命令显示的值。内核Makefile 的前四行定义了内核的名字。为了保护官方的内核不被破坏,Makefile 经过了修改,以生成一个与运行中的内核不同的名字。在一个模块插入运行中的内核前,这个模块必须针对运行中的内核进行编译。为此,您必须编辑内核的 Makefile。 例如,如果 uname -r 返回字符串2.6.18-1.2798.fc6,就将 EXTRAVERSION 定义从: EXTRAVERSION = -prep 修改为: EXTRAVERSION = -1.2798.fc6
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 18 也就是最后一个连字符后面的所有内容。
4.配置及编译内核。
进入/usr/src/2.6.18-1.2798.fc6-i586/目录下,可以看到Makefile文件,它包含了整个内核树编译信息。该文件最上面四行是关于内核版本的信息。对于整个Makefile可以不用做修改,采用默认的就可以了。
一般情况下,需要先用命令诸如"make menuconfig", "make xconfig"或者"make oldcofig"对内核进行配置,这几个都是对内核进行配置的命令,只是它们运行的环境不一样,执行一下这几个命令中的任何一个即可对内核进行配置: make menuconfig是基于界面的内核配置方法,make xconfig应该是基于QT库的,还有make gcofig也是基于图形的配置方法,应该是需要GTK的环境,make oldcofig就是对内核树原有的.config文件进行配置一下即可。
其实内核的配置部分,主要是保证内核启动模块可动态加载的配置,默认配置里面应该已经包含了这样的内容,因此,我用的是make menuconfig.
注意: 您的内核必须已经启用这些选项进行了编译,用以支持模块的动态加载。
(用make menuconfig调出内核配置菜单): Loadable module support ---> [*] Enable loadable module support [*] Module unloading [ ] Module versioning support (EXPERIMENTAL) [*] Automatic kernel module loading
注:该步没做时,在make modules时将出错。
Kernel Feautre -> Preemptible Kernel
[*]
注:该项为内核抢占式调度设置, 必须保证在构建运行的内核与编译环境的内核时都选上,一般PC机运行的内核已经选上了,否则在insmod时将出现如下错误:
# insmod st7565p_driver26.ko Using st7565p_driver26.ko st7565p_driver26: version magic '2.6.14.7 ARMv4 gcc-3.4' should be '2.6.14.7 preempt ARMv4 gcc-3.4' insmod: cannot insert `st7565p_driver26.ko': Invalid module format (-1): Exec format error
在内核源码的目录下执行:
# make
# make bzImage 编译内核
其中,第一个make也可以不执行,直接make bzImage。这个过程可能比较长,因为是对整个内核重新编译了。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。
然后执行:
# touch * // 时间或时区设置,源代码的时间戳比本机的时间更新,否则产生:make[2]: 警告:检测到时钟错误。您的创建可能是不完整的。
# make modules 编译模块
# make modules_install 安装编译
对内核的所有模块进行编译和安装。完成“内核树”的安装:
执行结束之后,会在/lib/modules下生成新的目录/lib/modules/2.6.18-1.2798.fc6/。 在随后的编译模块文件时,要用到这个路径下的build目录。至此,内核编译完成。 目录“/usr/src/2.6.18-1.2798.fc6-i586/”中就是所谓的“内核代码树”但是“/lib/modules/2.6.18-1.2798.fc6/build”是个符号链接,也指向这个目录,所以这里也可以叫做“内核代码树”,可以重启一下系统。
5.编写模块文件及Makefile
以LDD3上的hello.c为例:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk("hello,world.\n");
return 0;
}
static void hello_exit(void)
{
printk("Good bye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile如下:
obj-m := hello.o
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
上面的Makefile是这样确定内核源码树所在的目录的: 我们先到/lib/modules目录, 会看到一些以内核版本为名的目录, 目录中有一个build文件, 它是一个符号连接, 指向内核源码树. 那么如何确定进入哪个内核版本的目录呢? 这就可以通过 $ uname -r 来确定, 它指出了当前运行内核的版本.
上面的例子中只讨论了所有的代码在一个文件中的情况. 若代码分布在多个源文件中, 比如file1.c, file2.c, 生成hello.ko. 应该这样写Makefile: obj-m := hello.o hello-objs := file1.o file2.o 注意, 虽然我们的目的是生成.ko文件, 但在Makefile中写为.o!
hello.c和Makefile文件应该位于同一个目录下,可以放在/home下。
6.:编译和装载模块
在文件所处的目录下,执行:
# make 然后查看该目录下有哪些文件生成:可见,已经生成模块文件hello.ko.
hello.ko hello.mod.c hello.mod.o hello.o 运行命令:
# insmod hello.ko
应该可以看到返回的信息:Hello, world
以通过lsmod来查看模块是否加载进内核 然后再运行命令:
# rmmod hello
应该可以看到返回的信息:Goodbye! 如果没看到,就是输出到系统的日志文件中去了,可以查看文件: tail –f /var/log/messages
cat /var/log/syslog 应该有信息的输出。
以通过lsmod来查看该模块是否被卸载.
转载地址:http://pznvb.baihongyu.com/