Exam02_实验2 添加系统调用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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
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
第三行:进行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目录下另外