1. 实验目的

向现有 Linux 内核加入一个新的系统调用从而在内核空间中实现对用户空间的读写。 例如,设计并实现一个新的内核函数 mycall(),此函数通过一个引用参数的调用返回 当前系统时间,功能上基本与gettimeofday()相同。 也可以实现具有其它功能的系统调用。

2. 实验内容与步骤

1. 获取 Linux 内核源代码

http://www.kernel.org/ 下载Linux最新内核源代码(当前为3.17.3)

$ aria2c -c https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.17.3.tar.xz

拷贝至 /usr/src 文件夹并解压

# cp linux-3.17.3.tar.xz /usr/src
# cd /usr/src
# xz -d linux-3.17.3.tar.xz
# tar -xvf linux-3.17.3.tar

2. 添加新调用的源代码

kernel/sys.c 中添加相应的调用代码

asmlinkage int sys_mycall(struct timeval *tv) 
{
	struct timeval ktv;
	
	do_gettimeofday(&ktv);
	// 将内核空间的数据拷贝至用户空间
	if (copy_to_user(tv,&ktv,sizeof(ktv))) {
		return -EFAULT; 
	}

	return 0; 
}

3. 修改系统调用表

arch/x86/syscalls/syscall_64.tbl 中添加调用名和函数名

321	common	mycall	sys_mycall

四个参数分别代表:系统调用号、系统调用类型、系统调用名、调用函数

4. 编译内核

安装编译所需的工具

$ sudo apt-get install ncurses-dev

编译

# make mrproper
# make menuconfig
# make -j4
# make modules_install
# make install

5. 重启系统

$ sudo reboot

6. 编写程序测试

2.6.18版本之后的Linux内核中,_syscall()宏被删除,应使用syscall()函数。

test.c

#include <linux/unistd.h>
#include <linux/time.h> 
#define	MYCALL	321
int main()
{ 
	struct timeval gettime;
	struct timeval mycalltime;
	
	gettimeofday(&gettime,NULL);
	syscall(MYCALL, &mycalltime); 
	printf("gettimeofday:%lu %lu\n",
				gettime.tv_sec,gettime.tv_usec); 
	printf("mycall:%lu %lu\n",
				mycalltime.tv_sec,mycalltime.tv_usec);

	return 0;
}

3. 实验结果

内核编译过程

编译成功,新增系统调用源代码被正确编译。

测试程序运行结果

返回结果正确,新增的系统调用被成功添加进内核中。