实验目的
学习如何产生一个系统调用,以及怎样通过往内核中增加一个新函数,从而在内核空间中实现对用户空间的读/写。学习重建内核。
实验内容
- (1)设计并实现一个新的系统调用pedagogictime() ,该函数通过使用一个引用参数的调用返回当前的系统时间。
- (2)编写一个用户空间程序来测试pedagogictime()。
实验环境
系统版本:Ubuntu 14.04 LTS
内核版本: Linux 3.2.0.29-generic
欲编译内核:Linux-2.6.32.60
实验步骤
解压内核:
内核文件在文件目录Documents下,将压缩包解压到/usr/src/目录下。
- oslinux@oslinux-virtual-machine:~$ cd Documents
//进入Documents目录下
- oslinux@oslinux-virtual-machine:~/Documents$ sudo cp linux-2.6.32.60.tar. bz2 /usr/src/
//将压缩包拷贝到/usr/src/目录下
- oslinux@oslinux-virtual-machine :~/Documents$ cd /usr/src/
//进入 /usr/src/目录下
- oslinux@oslinux-virtual-machine:/usr/src$ sudo tar -jxvf linux-2.6.32.60.tar.bz2
//解压压缩包
2.添加系统调用:
(1)编写系统调用服务例程
编写加到内核中的源程序,即将要加到一个内核文件中去的一个函数,该函数的名称应该是新的系统调用名称前面加上sys_标志。
注意要在/usr/src/linux-2.6.32.60/kernel/sys.c文件中添加源代码。
//用编辑器打开sys.c文件添加系统调用pedagogictime的代码
oslinux@oslinux-virtual-machine:~$ sudo gedit /usr/src/linux-2.6.32.60/kernel/sys.c
//添加到sys.c的最后一行,代码如下:
asmlinkage long sys_pedagogictime(struct timeval *tv)
{
if(likely(tv)) {
struct timeval ktv;
do_gettimeofday(&ktv);
if(copy_to_user(tv,&ktv, sizeof(ktv)))
return -EFAULT;
}
return 0;
}
(2)添加系统调用号
为了从已有的内核程序中增加到新的函数的连接,需要编辑两个文件:
在/usr/src/linux-2.6.32.60/arch/x86/include/asm/unistd_32.h中增加新的系统调用号
同时还要将宏变量NR_syscalls的值加1 ;
oslinux@oslinux-virtual-machine:~$ sudo gedit/usr/src/linux-2.6.32.60/arch/x86/include
/asm/unistd_32.h
(3)修改系统调用表
在/usr/src/linux-2.6.32.60/arch/x86/kernel/syscall_table_32.S中增加新的内核函数的指针。
oslinux@oslinux-virtual-machine:~$ sudo gedit /usr/src/linux- 2.6.32.60/arch/x86/kernel
/syscall_table_32.S
(4)重新编译Linux内核
在当前工作目录(/usr/src/linux-2.6.32.60)进入超级用户,才可以重建内核。
oslinux@oslinux-virtual-machine:~$ cd /usr/src/linux-2.6.32.60
oslinux@oslinux-virtual-machine:/usr/src/linux-2.6.32.60$ sudo –s
现在开始编译内核。编译内核的基本过程为:
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make mrproper
//清除依赖
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60#cp /boot/config-3.2.0-29-generic-pae .config
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make oldconfig
//更改配置(一直回车)
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make clean
//清除中间文件
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make bzImage
//编译内核
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make modules
//编译内核模块
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make modules_install
//安装内核模块
root@oslinux-virtual-machine:/usr/src/linux-2.6.32.60# make install
//安装内核
(5)启用新内核
重新启动ubuntu,开机时长按shift键直到进入启动加载页面,选择新编译的内核版本,按回车键确认选择并进入系统。
查看内核版本,是否为新内核版本:oslinux@oslinux-virtual-machine:~$ uname –a
(6)测试
编写测试程序,测试新的系统调用是否添加成功。
5.实验结果
结果展示
查看版本结果
在新版本内核下运行代码结果
结果解读
查看内核版本为新内核版本,说明内核切换成功。
运行测试程序调用新添加的函数,返回当前的系统时间。
核心代码
#include <sys/syscall.h>
#include <linux/unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
main(){
struct timeval tv;
long tmp;
tmp = syscall(337, &tv);
if(tmp != -1){
printf("First, user get tv_sec:%d\n", (int)tv.tv_sec);
}
}