1. 引言
本文档解释了怎样在Linux2.6中实现一个新的系统调用。一个系统调用被应用程序用来向操作系统请求服务。
2. 系统调用
一个系统调用被应用程序用来向操作系统请求服务。下面的语句阐述了为什么需要
系统调用。一个操作系统可以直接访问一个系统的硬件,但是一个用户程序没有直接访问硬件的权限。这样做是为了使系统免于遭受恶意的用户程序的破坏,保证系统的安全和保密。但是,经常,一个用户程序需要一些硬件相关的信息(例如,从一个摄像头中获取数据以显示图像),但是,它不能直接获取信息。因此,它请求操作系统提供它这些信息。这个请求是通过使用一个合适的系统调用来完成的。
一个系统调用可以在内核模式下执行。每个系统调用有一个与之关联的数字。这个数字被传递给内核,内核是根据这个数字来决定该执行哪个系统调用。当一个用户程序发出一个系统调用时,它实现上是调用一个库函数。该库函数通过执行汇编指令INT0x80向Linux操作系统发出一个陷阱。它同时也通过EAX寄存器将系统调用号传递给内核。系统调用的参数通过其他的寄存器(EBX,ECX,etc)也传递给内核。内核执行该系统调用,并通过一个寄存器将执行结果返回给用户程序。如果该系统调用需要返回给用户程序大量的数据,它会使用其他的机制(例如,copy_to_user调用)。
3. 需要修改或创建的文件列表
假设你的Linux源码的基目录是/usr/src/linux.需要修改的内核文件如下所示:
1./usr/src/linux/arch/i386/kernel/syscall_table.S
2./usr/src/linux/include/asm-i386/unistd.h
3./usr/src/linux/include/linux/syscalls.h
4. /usr/src/linux/Makefile
需要新创建的文件/目录如下所示:
1. /usr/src/linux/mycall –包含我们自己的系统调用的源文件,头文件和Makefile的目录(你也可以在一个已存在的文件中实现自己的系统调用).
2. /usr/src/linux/mycall/mycall.c–实现我们自已的系统调用的源码.
3. /usr/src/linux/mycall/Makefile- Makefile
新创建的用户空间文件以及用于测试我们的系统调用的文件列表如下所示:
1. testmycall.c –调用我们自己的系统调用的源码.
2. testmycall.h –头文件.
4. 需要修改的内核文件
文件的全路径-/usr/src/linux/arch/i386/kernel/syscall_table.S
该文件包含系统调用的名字.
1.在该文件的后面添加一行(假设我们的系统调用名是mycall).
2. 添加“.longsys_mycall”列表的尾部.
4. 2. unistd.h
文件全路径-/usr/src/linux/include/asm-i386/unistd.h
该文件包含系统调用号,当一个系统调用被调用时通过寄存器(EAX)将其传递给内核.
在列表后面添加“#define__NR_mycall Last_System_Call_Num + 1 ”
如果上一个系统调用定义在这里:
“#define__NR_vmsplice 316”,那么在列表后面添加:
“#define__NR_mycall 317”.
将“NR_syscalls”增1.这样,如果NR_syscalls定义如下:
“#defineNR_syscalls 317”,那么将其改为:
“#defineNR_syscalls 318”
4. 3. syscalls.h
文件全路径-/usr/src/linux/include/linux/syscalls.h
该文件包含了系统调用的声明.
在该文件的末尾加入如下语句:
“asmlinkagelong sys_mycall(int i);”
4.4. Makefile
文件全路径-/usr/src/linux/Makefile
Add mycall/ to core-y (Search for regex: core-y.*+=). You will be creating this directory.
This directory will contain the source file, header file and the Makefile for our system call.
5. 新创建的内核文件/目录5.1mycall
文件全路径-/usr/src/linux/mycall
在/usr/src/linux创建一个新目录,并命名为“mycall”.
5.2. mycall.c
文件全路径-/usr/src/linux/mycall/mycall.c
在目录“mycall”下创建源码文件“mycall.c”.mycall.c包含系统调用的源码.源文件中系统调用的定义是asmlinkagelong sys_mycall(…){…} . 它应该包含linux/linkage.h.因此,源文件“mycall.c”看起来是这样的:
/*—Startof mycall.c—-*/
#include<linux/linkage.h>
asmlinkagelong sys_mycall(int i)
{
returni+10;
}
/*—Endof mycall.c——*/
什么是asmlinkage?
asmlinkage用于在内核栈中查找参数.
5.3. Makefile
文件全路径-/usr/src/linux/mycall/Makefile
目录“mycall”下的Makefile只有一行:
#####MakefileStart#####
obj-y:= mycall.o
#####MakefileEnd#######
6.新创建的用于测试我们自己的系统调用的用户空间文件
6.1testmycall.h(新创建的用户空间头文件)
往该头文件中加三行代码.testmycall.c(新创建的用户空间源文件)
testmycall.h
创建一个头文件testmycall.h.该头文件将被任何调用我们的系统调用的程序所包含.
第1行:它是必须的,因为我们需要__syscall1的定义.
#include<linux/unistd.h>
第2行:这也是需要的,因为我们需要系统调用号.
#define__NR_mycall 317
第3行:这对于拥有一个参数的系统调用是必须的.下面我们会详述.
_syscall1(long,mycall, int, i)
因此,我们的头文件看起来是这样的:
/*—Startof header file——*/
#include<linux/unistd.h>
#define__NR_mycall 317
_syscall1(long,mycall, int, i)
/*—Endof header file——–*/
6.2. testmycall.c(新创建的用户空间源文件)
testmycall.c
在与testmycall.h同一目录下创建一个C文件testmycall.c.C文件看起来如下所示:
/*—Startof C file——*/
#include<stdio.h>
#include”testmycall.h”
intmain(void)
{
printf(“%d\n”,mycall(15));
}
/*—Endof C file——*/
6.3. __syscallN宏
longmycall(int i)
{
returnsyscall(__NR_mycall, i);
}
但是,宏_syscallN宏在内核中是不同的.你可以在/usr/src/linux/include/asm-i386/unistd.h中查看定义.
7. 测试我们新的系统调用
Step 1:重新编译和安装新的内核,使得操作系统中包含进我们的系统调用.
Step 2:编译和执行用户空间C文件(testmycall.c).
RESULT:你应该得到的输出是25. 这已经在内核2.6.17.13上测试过.