在Linux内核中添加系统调用,并编译内核
1 环境准备
运行系统:vmware下安装的ubuntu10.10 32bit桌面版。
编译内核版本: linux-2.6.32.63
内核目录: /home/wanchouchou/linuxKernel/linux-2.6.32.63
为了方便,使用chmod 777 -R linux-2.6.32.63/ 将所有的内核文件都改为全权限,这样就可以在非root状态修改文件进行编译了。
2 添加系统调用
首先进入linux-2.6.32.63目录下。以后的文件路径都是以此目录为“根目录”的。
2.1 在系统函数表中添加表项
进入arch/x86/kernel目录下,然后vim syscall_table_32.S,在此文件的最后一行添加自己的系统调用表项:
1 .long sys_rt_tgsigqueueinfo /* 335 */ 2 .long sys_perf_event_open 3 .long sys_mycall //这是我们自己添加的表项
好了,下面开始添加系统调用号。
2.2 添加自己的系统调用号
现在进入目录 arch/x86/include/asm,该目录下有三个文件unistd_32.h, unistd_64.h, unistd.h。由于我们编译的是32位内核,所以需要在unistd_32.h中添加系统调用号。
vim unistd_32.h,在最后添加代码:
1 #define __NR_perf_event_open 336 2 #define __NR_mycall 337 //添加的 3 #ifdef __KERNEL__ 4 5 /* 原本为337,但是由于我们添加了一行,所以改338*/ 6 #define NR_syscalls 338
2.3 编写自己系统调用的实现函数
由于系统调用必须编译到核心的内核镜像中去。所以我们将此函数的实现写到kernel/sys_i386_32.c中:
1 //引入头文件 2 #include <asm/page.h> 3 ........ 4 //添加函数代码 5 asmlinkage long sys_mycall(void){ 6 return THREAD_SIZE; 7 }
3 编译内核
现在就可以回到linux-2.6.32.63目录进行编译了。如果以前编译过内核,最好使用make mrproper和make clean命令来清除之前的编译残留文件。如果是第一次编译内核,那么就需要对内核的编译选项进行配置,这里推荐使用make menuconfig。如果键入该命令后提示缺少ncurses库文件的话,就使用sudo apt-get install libncurses5-dev命令安装该库。之后就可以进行menuconfig配置了,建议保持默认值即可。
一切准备就绪,键入make命令开始编译内核!现在可以泡一桶coffee来慢慢喝了,为什么是一桶?因为时间真的很长~~~
4 编译并安装内核模块
make modules ,这个相对来说要快一点。10分钟左右。然后安装内核模块: make modules_install 以及安装内核: make install
5 让新旧内核均可以加载
sudo mkinitramfs -o /book/initrd.img-2.6.32.63 //此命令会在/boot目录下生成initrd.img-2.6.32.63等文件
sudo update-initramfs -c -k 2.6.32.63 //根据/lib/modules/2.6.32.63文件进行更新。如果此命令出现FATAL: could not load /lib/modules/2.6.32.63的话,就将命令后面的2.6.32.63改为此目录下的新内核文件名即可(注意:旧内核文件名为2.6.35-22-generic)。
sudo update-grub2 //自动修改系统的引导配置文件,主要是更新/book/grup/grup.cfg启动文件。此命令执行完后会在grup.cfg中添加新内核的启动项
6 进入grup选择需要启动的内核
在ubuntu10.10默认grup菜单是不会显示的,也就是说无法选择启动的内核。要想显示的话,需要按照如下步骤进行操作:
1、把/etc/default/grub文件中的GRUB_HIDDEN_TIMEOUT=0改为大于0的数字,比如4;
2、把/etc/grub.d/30_os-prober文件中所有的set timeout=0中的0改为10.仔细找,不要漏过了;
3、上述修改完成后,执行命令更新grup2: sudo update-grub //更新/boot/grub/grub.cfg文件
7 重启
reboot。这时候在启动的时候就会停留在grup界面了,里面有4个选项,每个内核对应2个,选择进入2.6.32.63内核。启动完成后在终端输入: uname -a 显示的就是新内核版本了。
8 验证自定义的系统调用
现在我们可以编写代码来验证自定义的系统调用是否被加入到了内核中:
1 #include<sdtio.h> 2 #include<unistd.h> 3 #include<sys/syscall.h> 4 5 #define SYS_mycall 337 //同我们前面定义的系统调用号相同 6 7 int main(){ 8 long ret = 0; 9 ret = syscall(SYS_mycall); //根据系统调用号调用对应的系统调用 10 printf("ThreadSize is: %ld\n", ret); 11 return 0; 12 }
需要说明的是,在2.6.20以后的版本是没有以前的那些系统调用宏定义的(如_syscall0, _syscall1等),必须使用syscall来进行系统调用(可以通过man syscall获取参考信息)。同时由于是系统调用,所以需要root模式下编译、运行程序。程序运行结果: 8192。
注意,按理来说,我们是不需要自己定义SYS_mycall = 337的,但是,如果不这样的话编译的时候会提示找不到SYS_mycall,也就是说,它并没有被显示地加入到syscall.h文件中~解决办法如下:
1、修改/usr/include/asm/unistd_32.h文件,将最后的#define __NR_recvmmsg 337 改为 #define __NR_mycall 337 //貌似系统中根本就没有recvmmsg只有recvmsg~
2、修改/usr/include/bits/syscall.h文件,在文件开头位置加入代码: #define SYS_mycall __NR_mycall ;
这样,我们就可以直接在程序中使用SYS_mycall了。