最近工作要在内核态编程,学习了一下自己编译内核,流程如下:
1. #cd /usr/src/
拷贝一份 linux 内核代码
#cp -r linux-2.6.32.12-0.7 zcl-linux-2.6.32.84-0.7
如果系统中没有安装内核代码,则可以去gnu官网上下载一份最新的内核代码
2. cd 到自己的 linux 内核代码目录中
#cd zcl-linux-2.6.32.84-0.7
#make mrproper //删除原来编译产生的垃圾
#make menuconfig //文本界面配置内核编译选项,也可用更原始的 #make config
在弹出界面中选中自己需要编译的模块,比如在
Genral setup--> 中选择 GCOV 功能等,选择完后,退出;
Genral setup 还可以选择编译好的内核 lib 安装路径,Local Version,可在后面填加字符串
这样就可以将编译好的模块安装到不同目录中
3. #vi Makefile
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 16
EXTRAVERSION = .84 //将扩展版本号修改一下,以便和系统中原本内核版本区分
NAME=zhangcunli
2.6 的内核代码编译只需要执行一步即可
#make 但这样编译时间太长,可以按如下步骤执行:
//可以替换为如下步骤:
#make bzImage /*编译大内核*/
或者 male -jn bzImage ,其中 n 的值为 2*cpu核的个数,这样可以加快编译速度
比如,我的电脑有2个cpu,每个cpu都是双核,所以我使用#make -j8 bzImage编译
或者#make zImage 编译压缩形式的内核
#make modules //编译选择的模块,或者使用 make -j8 modules
#make modules_install
将编译出来的模块安装到系统指定目录中
/lib/modules/2.6.32.84-0.7-default 其中 /lib/modules后面的一级目录为自己先前设置版本号
4. 编译成功后,就可以安装内核了
#cp /boot/grub/menu.lst /boot/grub/menu.lst.bak 备份一下grub引导配置文件
#make install
该命令会做如下事情:
(1)把压缩内核映象拷贝到/boot目录下, 并创建相应的System.map符号链接;
(2)修改bootloader的配置文件;
(3)调用mkinitrd程序创建内核的initrd映象. 对于GRUB而言,
将在/boot/grub/menu.lst配置文件自动增加如下类似的配置行:
title zhangcunli SUSSE10 SP2 10.2-0 - 2.6.3216.84-0.7
root (hd0,1)
kernel /boot/vmlinuz-2.6.32.84-0.7-default root=/dev/sda2 vga=0x314 resume=/dev/sda1 splash=silent showopts
initrd /boot/initrd-2.6.32.84-0.7-default
自己再修改一下新填加进来的配置行 title
最后,修改 menu.lst 的default 项,default 的值从 0 开始算起。
5. 最后终结一下,编译自己的内核所需要的步骤:
5.1: 拷贝一份内核代码
5.2: make menuconfig 选择配置,vi Makefile 修改扩展版本号
5.3: make bzImage 编译内核
5.4: make modules 编译模块 【或者只执行一次make编译所有】
5.5: make moduels_install 将编译好的模块安装到 /lib/modules 下
5.6: make install 安装编译好的内核版本
5.7: 修改 /boot/grub/menu.lst 将自己的内核设置为默认启动项
记:自己编译的时候,一直没敢执行 make install 这一步,生怕这一步会替换掉系统原有的内核,以及如何生成/boot/initrd-2.6.32.84-0.7-default这个文件,一直在找如何通过修改参数将内核安装到指定目录;【应该是修改 Makefile 下面的几个宏可以实现】,最后发现只要修改的 Makefile 里面的版本号,make install 会自动将生成的内核文件按版本号命名,并且这一步会生成 initrd 文件,不需要手动使用 mkinitrd 命令生成。
注意啦:当我在公司的suse11上编译新内核的时候,却发现如上步骤在启动新内核时失败,提示:unsupported ko 之类的错误,经过实验发现可用如下步骤安装成功:
执行完5.5后,
5.6: #cp /usr/src/zcl-linux-2.6.32.84-0.7/arch/boot/x86/bzImage /boot/vmlinuz-2.6.32.84-0.7
#cp /usr/src/zcl-linux-2.6.32.84-0.7/System.map /boot
#cp /usr/src/.config /boot
#vi /etc/modprobe.d/unsupported****
里面应该只有一个值 allow-unsupported- (想不起叫什么名字的)由 0 改成 1;
#cd /boot
#mkinitrd
该命令会根据 /boot 下所有的 vmlinuz 文件生成对应的 initrd 文件,并且写系统映射表;
5.7:修改 /boot/grub/menu.lst ,这回得拷贝一条记录,将其中的版本号改为刚才自己编译好的;reboot即可成功;
编写一个最简单的 xxx.ko 模块,使用 insmod 插入到内核中,再使用 rmmod 从内核中移除:
1. helloko.c 文件内容如下:
#include
#include
#include
MODULE_LICENSE("GPL");
static int __init helloko_init()
{
/* KERN_ALERT 表示以高优先级将打印输出的控制台,注:X界面环境不行*/
printk(KERN_ALERT"Hello, World - this is the kernel speaking\n");
return 0;
}
static void __exit helloko_exit()
{
printk(KERN_ALERT"Short is the life of a kernel module\n");
}
module_init(helloko_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangcunli");
这个模块定义了两个函数, 一个在模块加载到内核时被调用(helloko_init)以及一个在模块被去除时被调用( helloko_exit). moudle_init 和 module_exit 这几行使用了特别的内核宏来指出这两个函数的角色. 另一个特别的宏 (MODULE_LICENSE) 是用来告知内核, 该模块带有一个自由的许可证; 没有这样的说明, 在模块加载时内核会抱怨.
2. Makefile 文件如下:
obj-m:=helloko.o
KERNELBUILD := /lib/modules/`uname -r`/build
default:
@echo "BUILD kmod"
@make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
@echo "CLEAN kmod"
@rm -rf *.o *.ko
@rm -rf .depend .*.cmd *.mod.c .tmp_versions *.symvers .*.d *.markers *.order
3. 在 helloko.c 所在目录执行
#make
验证:
#insmod helloko.ko 插入 helloko.ko 模块
#dmesg | tail 查看日志
可以看到日志输出
#rmmod helloko.ko 移除 helloko.ko 模块
#dmesg | tail
或者
#tail /var/log/messages 查看日志