Exam02_实验2 添加系统调用

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

添加系统调用

实验目的

学习Linux内核的系统调用,理解、掌握Linux系统调用的实现框架、用户界面、参数传递、进入/返回过程。

实验内容

本实验分两步走。

第一步,在系统中添加一个不用传递参数的系统调用;执行这个系统调用,使用户的uid等于0。显然,这不是一个有实际意义的系统调用。我们的目的并不是实用不实用,而是通过最简单的例子,帮助熟悉对系统调用的添加过程,为下面我们添加更加复杂的系统调用打好基础。

第二步,用kernel module机制,实现系统调用gettimeofday的简化版,返回调用时刻的日期和时间。

实验指导

2.1一个简单的例子

在我们开始学习系统调用这一章之前,让我们先来看一个简单的例子。就好像哪个经典的编程书上都会使用到的例子一样:

1: int main(){

2: printf(“Hello World!\n”);

3: }

我们也准备了一个例子给你:

1: #include /* all system calls need this header */

2: int main(){

3: int i = getuid();

4: printf(“Hello World! This is my uid: %d\n”, i);

5: }

这就是一个最简单的系统调用的例子。与上面那个传统的例子相比,在这个例子中多了

2行,他们的作用分别是:

第一行:包括unistd.h这个头文件。所有用到系统调用的程序都需要包括它,因为系统调用中需要的参数(例如,本例中的“__NR_getuid”,以及_syscall0()函数)包括在unistd.h中;根据C语言的规定,include 意味着/usr/include/linux目录下整个unistd.h都属于Hello World源程序了。

第三行:进行getuid()系统调用,并将返回值赋给变量i。

好了,这就是最简单的一个使用了系统调用的程序,现在你可以在你的机器上试一试它。然后我们一起进入到系统调用的神秘世界中去。

2.2 简单系统调用的添加

在这一节中,我们将要实现一个简单的系统调用的添加。我们先给出题目:

题目:在现有的系统中添加一个不用传递参数的系统调用。

功能要求:调用这个系统调用,使用户的uid等于0。

目的:显然,这不是一个有实际意义的系统调用,我们的目的并不是有用,而是一种证明,一个对系统调用的添加过程的熟悉,为下面我们添加更加复杂

的系统调用打好基础。

怎么样?觉得困难还是觉得太简单?我们首先承认,每个人接触Linux的时间长短不一样,因此基础也会有一点差别。那么对于觉得太简单的人呢,请你迅速地合上书本,然后跑到电脑前面,开始实现这个题目。来吧,不要眼高手低,做完之后,你就可以跳过这一节,直接进入下一节的学习了。对于觉得有点困难的人呢,不用着急,这一节就是专门为你准备的。我们会列出详细的实现步骤,你一定也没有问题的。

如果你前面对整个系统调用的过程有一个清晰的理解的话,我们就顺着系统调用的流程思路,给出一个添加新的系统调用的步骤:

2.2.1决定你的系统调用的名字

这个名字就是你编写用户程序想使用的名字,比如我们取一个简单的名字:mysyscall。一旦这个名字确定下来了,那么在系统调用中的几个相关名字也就确定下来了。

●系统调用的编号名字:__NR_mysyscall;

●内核中系统调用的实现程序的名字:sys_mysyscall;

现在在你的用户程序中出现了:

#include

int main()

{

mysyscall();

}

流程转到标准C库。

2.2.2利用标准C库进行包装吗

编译器怎么知道这个mysyscall是怎么来的呢?在前面我们分析的时候,我们知道那时标准C库给系统调用作了一层包装,给所有的系统调用做出了定义。但是显然,我们可能

不愿意去改变标准C库,也没有必要去改变。那么我们在自己的程序中来做:

#include

_syscall0(int,mysyscall) /* 注意这里没有分号*/

int main()

{

mysyscall();

}

好,由于有了_syscall0这个宏,mysyscall将得到定义。但是现在系统会去找系统调用号,以放入eax。所以,接下来我们定义系统调用号。

2.2.3添加系统调用号

系统调用号在文件unistd.h里面定义。这个文件可能在你的系统上会有两个版本:一个是C库文件版本,出现的地方是在/usr/include/unistd.h和/usr/include/asm/unistd.h;另外还有一个版本是内核自己的unistd.h,出现的地方是在你解压出来的2.6.15内核代码的对应位置(比如/usr/src/linux/include/linux/unistd.h和/usr/include/asm-i386/unistd.h)。当然,也有可能这个C库文件只是一个到对应内核文件的连接。现在,你要做的就是在文件unistd.h中添加我们的系统调用号:__NR_mysyscall,如下所示:

include/asm-i386/unistd.h

/usr/include/asm/unistd.h

231 #define __NR_mysyscall223 /* mysyscall adds here */

/* 注意:不同版本的内核系统调用号不一样,您可以根据内核版本不同对系统调用号进行修改*/

添加系统调用号之后,系统才能根据这个号,作为索引,去找syscall_table中的相应表项。所以说,我们接下来的一步就是:

2.2.4在系统调用表中添加相应表项

我们前面讲过,系统调用处理程序(system_call)会根据eax中的索引到系统调用表(sys_call_table)中去寻找相应的表项。所以,我们必须在那里添加我们自己的一个值。

arch/i386/kernel/syscall_table.S

……

233 .long sys_mysyscall

234 .long sys_gettid

235 .long sys_readahead /* 225 */

……

到现在为止,系统已经能够正确地找到并且调用sys_mysyscall。剩下的就只有一件事情,那就是sys_mysyscall的实现。

2.2.5 sys_mysyscall的实现

我们把这一小段程序添加在kernel/sys.c里面。在这里,我们没有在kernel目录下另外

相关文档
最新文档