Linux内核添加系统调用
一、环境说明
?操作系统:Ubuntu 11.04
?旧内核版本:2.6.38.9
?新内核版本:2.6.39
?编译器版本:GCC 4.5.2
二、实验目的
?掌握内核的编译方法
?深入理解内核编译的原理
?掌握给内核添加系统调用的方法
?了解系统调用的运行机制
?学会在用户态调用系统调用
三、实验原理
系统调用:Linux内核中用户实现系统程序的一组子程序
一个程序的虚拟地址空间分为用户态和内核态,而用户进程要想进入内核就要通过系统调用或以内核模块的形式添加到内核。
下图展示了系统调用的实现过程(注:该图来源于网络资料):
1、C程序通过包含头文件#include<syscall.h>通过源文件追踪,找到syscall()函数其实位/usr/include/unistd.h中
如下:
extern long int syscall (long int __sysno, ...) __THROW;
该函数使用INT 0x80软中断内核将系统调用号传递给保存到eax中,然后将按系统调用号来查找系统调用表中的相应index的函数入口地址。来执行服务例程。
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
其实是通过宏定义定义系统调用函数
位于./kernel/sys.c中
SYSCALL_DEFINE1(my_traverse_process,int,num)
{
..................................
}
SYSCALL_DEFINE1后的‘1’它表示该系统调用有一个参数,后面参数的含义依次是函数名、参数类型、参数名
该函数执行完毕后,执行syscall_exit并将值返回。
INT 0x80之所以能够执行,因为系统在启动时将0x80设置了系统调用中断门。
三、步骤索引
?编译升级内核
?添加系统调用后重新编译内核
?编写用户程序调用自己添加的系统调用
四、实验步骤
1、编译内核
?从下载Linux内核版本2.6.39
?解压内核到任意目录(这里不用必须解压到/usr/src/下,Linus也说不必那样做,况且自己也试验成功)
(*)注:这样有个缺点,那就是在装玩模块后,系统会在/lib/modules/2.6.39/下创建该源码的软链接用于以后用户编译内核使用,当用户无意将其删除时,将会造成无法编译自己的模块。
?进入内核目录
?首先配置make menuconfig配置裁剪内核(如果没有什么必要可以复制/boot/config-***,这是现在的内核配置)
cp /boot/config-*** ./.config
?开始编译,可以执行make all,当然也可以分步完成
make dep
make clean
make bzImahe
make modules
?安装内核驱动
make modules_install
?安装内核
make install
?生成引导镜像文件,并将其复制到/boot目录下
mkinitramfs -o initrd.img-2.6.39 2.6.39
cp initrd.img-2.6.39
?最后更新grub
update-grub
?启动新的内核,成功。
2、修改内核源码,添加系统调用
?首先打开文件./arch/x86/include/asm/unistd_32.h在最后添加系统调用号,如下(最后一行):
#define __NR_name_to_handle_at 341
#define __NR_open_by_handle_at 342
#define __NR_clock_adjtime 343
#define __NR_syncfs 344
#define __NR_my_traverse_process 345 /*This is added by yan,this is a test for system call*/
?打开./arch/x86/kernel/syscall_table_32.S(最后一行)
.long sys_prlimit64/* 340 */
.long sys_name_to_handle_at
.long sys_open_by_handle_at
.long sys_clock_adjtime
.long sys_syncfs
.long sys_my_traverse_process
?打开./include/linux/syscalls.h添加系统调用函数的声明
asmlinkage long sys_my_traverse_process(int num); /*This is added by yan*/
?打开./kernel/sys.c文件添加其实现函数
SYSCALL_DEFINE1(my_traverse_process,int,num)
{
struct task_struct *pos;
struct list_head *current_head;
int count=0;
printk("Traversal module is working..\n");
current_head=&(current->tasks);
list_for_each_entry(pos,current_head,tasks)
{
count++;
printk("[process %d]: %s\'s pid is %d\n",count,pos->comm,pos->pid);
}
printk(KERN_ALERT"The number of process is:%d\n",count);
printk(KERN_ALERT"This is a sample test output from kernel!\nAnd this is added by yan\n");
return (long)num;
}
注:该函数的功能是遍历系统中的进程,并输出进程名和进程号,并返回输入的参数值。