实验二系统调用

上传人:wm****3 文档编号:43526997 上传时间:2018-06-06 格式:DOC 页数:12 大小:65.50KB
返回 下载 相关 举报
实验二系统调用_第1页
第1页 / 共12页
实验二系统调用_第2页
第2页 / 共12页
实验二系统调用_第3页
第3页 / 共12页
实验二系统调用_第4页
第4页 / 共12页
实验二系统调用_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《实验二系统调用》由会员分享,可在线阅读,更多相关《实验二系统调用(12页珍藏版)》请在金锄头文库上搜索。

1、实验目的实验目的建立对系统调用接口的深入认识 掌握系统调用的基本过程 能完成系统调用的全面控制 为后续实验做准备实验内容实验内容此次实验的基本内容是:在 Linux 0.11 上添加两个系统调用,并编写两个简单的应用程序测试它们。iam()第一个系统调用是 iam(),其原型为:int iam(const char * name);完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。要求 name 的长度不能超过23 个字符。返回值是拷贝的字符数。如果 name 的字符个数超过了 23,则返回“-1”,并置errno 为 EINVAL。在 kernal/who.c 中实现此系统调用

2、。whoami()第二个系统调用是 whoami(),其原型为:int whoami(char* name, unsigned int size);它将内核中由 iam()保存的名字拷贝到 name 指向的用户地址空间中,同时确保不会对 name越界访存(name 的大小由 size 说明)。返回值是拷贝的字符数。如果 size 小于需要的空间,则返回“-1”,并置 errno 为 EINVAL。也是在 kernal/who.c 中实现。测试程序测试程序运行添加过新系统调用的 Linux 0.11,在其环境下编写两个测试程序 iam.c 和 whoami.c。最终的运行结果是:$ ./iam

3、lizhijun $ ./whoami lizhijun 实验报告实验报告在实验报告中回答如下问题:1.从 Linux 0.11 现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗? 2.用文字简要描述向 Linux 0.11 添加一个系统调用 foo()的步骤。评分标准评分标准将 testlab2.c 在修改过的 Linux 0.11 上编译运行,显示的结果即内核程序的得分。满分 50% 只要至少一个新增的系统调用被成功调用,并且能和用户空间交换参数,可得满分将脚本 testlab2.sh 在修改过的 Linux 0.11 上运行,显示的结果即应用程序的得分。满分 3

4、0% 实验报告,20%实验提示实验提示首先,请将 Linux 0.11 的源代码恢复到原始状态。注释的 5.5 节详细讲述了 0.11 如何处理系统调用,是非常有价值的参考。操作系统实现系统调用的基本过程是:1.应用程序调用库函数(API); 2.API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态; 3.内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用); 4.系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数; 5.中断处理函数返回到 API 中; 6.API 将 EAX 返回给应用程序。应用程序如何调用系统调用应用程序如何调用系统调用在通常情况下

5、,调用系统调用和调用一个普通的自定义函数在代码上并没有什么区别,但调用后发生的事情有很大不同。调用自定义函数是通过 call 指令直接跳转到该函数的地址,继续运行。而调用系统调用,是调用系统库中为该系统调用编写的一个接口函数,叫API(Application Programming Interface)。API 并不能完成系统调用的真正功能,它要做的是去调用真正的系统调用,过程是:把系统调用的编号存入 EAX 把函数参数存入其它通用寄存器 触发 0x80 号中断(int 0x80)0.11 的 lib 目录下有一些已经实现的 API。Linus 编写它们的原因是在内核加载完毕后,会切换到用户模

6、式下,做一些初始化工作,然后启动 shell。而用户模式下的很多工作需要依赖一些系统调用才能完成,因此在内核中实现了这些系统调用的 API。我们不妨看看 lib/close.c,研究一下 close()的 API:#define _LIBRARY_ #include _syscall1(int,close,int,fd)其中_syscall1 是一个宏,在 include/unistd.h 中定义。将_syscall1(int,close,int,fd)进行宏展开,可以得到:int close(int fd) long _res; _asm_ volatile (“int $0x80“ : “

7、=a“ (_res) : “0“ (_NR_close),“b“ (long)(fd); if (_res = 0)return (int) _res; errno = -_res; return -1; 这就是 API 的定义。它先将宏_NR_close 存入 EAX,将参数 fd 存入 EBX,然后进行 0x80中断调用。调用返回后,从 EAX 取出返回值,存入_res,再通过对_res 的判断决定传给API 的调用者什么样的返回值。其中_NR_close 就是系统调用的编号,在 include/unistd.h中定义:#define _NR_close 6所以添加系统调用时需要修改 in

8、clude/unistd.h 文件,使其包含_NR_whoami 和_NR_iam。而在应用程序中,要有:#define _LIBRARY_/* 有它, _syscall1 等才有效。详见 unistd.h */ #include /* 有它,编译器才能获知 自定义的系统调用的编号 */ _syscall1(int, iam, const char*, name);/* iam()在用户空间的接口函数 */ _syscall2(int, whoami,char*,name,unsigned int,size); /* whoami()在用户空间的接口函数 */在 0.11 环境下编译 C 程序

9、,包含的头文件都在/usr/include 目录下。该目录下的 unistd.h 是标准头文件(它和 0.11 源码树中的 unistd.h 并不是同一个文件,虽然内容可能相同),没有_NR_whoami 和_NR_iam 两个宏,需要手工加上它们,也可以直接从修改过的 0.11 源码树中拷贝新的 unistd.h 过来。从从“int 0x80”进入内核函数进入内核函数int 0x80 触发后,接下来就是内核的中断处理了。先了解一下 0.11 处理 0x80 号中断的过程。在内核初始化时,主函数(在 init/main.c 中,Linux 实验环境下是 main(),Windows 下因编译器

10、兼容性问题被换名为 start())调用了 sched_init()初始化函数:void main(void) time_init();sched_init();buffer_init(buffer_memory_end); sched_init()在 kernel/sched.c 中定义为:void sched_init(void) set_system_gate(0x80, set_system_gate 是个宏,在 include/asm/system.h 中定义为:#define set_system_gate(n,addr) _set_gate( extern int sys_iam

11、();不然,编译会出错的。实现实现 sys_iam()和和 sys_whoami()添加系统调用的最后一步,是在内核中实现函数 sys_iam()和 sys_whoami()。每个系统调用都有一个 sys_xxxxxx()与之对应,它们都是我们学习和模仿的好对象。比如在fs/open.c 中的 sys_close(int fd):int sys_close(unsigned int fd) return (0); 它没有什么特别的,都是实实在在地做 close()该做的事情。所以只要自己创建一个文件:kernel/who.c,然后实现两个函数就万事大吉了。修改修改 Makefile要想让我们添

12、加的 kernel/who.c 可以和其它 Linux 代码编译链接到一起,必须要修改 Makefile文件。Makefile 里记录的是所有源程序文件的编译、链接规则,注释3.6 节有简略介绍。我们之所以简单地运行 make 就可以编译整个代码树,是因为 make 完全按照 Makefile 里的指示工作。Makefile 在代码树中有很多,分别负责不同模块的编译工作。我们要修改的是kernel/Makefile。需要修改两处。一处是:OBJS = sched.o system_call.o traps.o asm.o fork.o panic.o printk.o vsprintf.o s

13、ys.o exit.o signal.o mktime.o改为:OBJS = sched.o system_call.o traps.o asm.o fork.o panic.o printk.o vsprintf.o sys.o exit.o signal.o mktime.o who.o另一处:# Dependencies: exit.s exit.o: exit.c ./include/errno.h ./include/signal.h ./include/sys/types.h ./include/sys/wait.h ./include/linux/sched.h ./includ

14、e/linux/head.h ./include/linux/fs.h ./include/linux/mm.h ./include/linux/kernel.h ./include/linux/tty.h ./include/termios.h ./include/asm/segment.h改为:# Dependencies: who.s who.o: who.c ./include/linux/kernel.h ./include/unistd.h exit.s exit.o: exit.c ./include/errno.h ./include/signal.h ./include/sy

15、s/types.h ./include/sys/wait.h ./include/linux/sched.h ./include/linux/head.h ./include/linux/fs.h ./include/linux/mm.h ./include/linux/kernel.h ./include/linux/tty.h ./include/termios.h ./include/asm/segment.hMakefile 修改后,和往常一样“make all”就能自动把 who.c 加入到内核中了。如果编译时提示who.c 有错误,就说明修改生效了。所以,有意或无意地制造一两个错误

16、也不完全是坏事,至少能证明 Makefile 是对的。用用 printk()调试内核调试内核oslab 实验环境提供了基于 C 语言和汇编语言的两种调试手段。除此之外,适当地向屏幕输出一些程序运行状态的信息,也是一种很高效、便捷的调试方法,有时甚至是唯一的方法,被称为“printf 法”。要知道到,printf()是一个只能在用户模式下执行的函数,而系统调用是在内核模式中运行,所以 printf()不可用,要用 printk()。它和 printf 的接口和功能基本相同,只是代码上有一点点不同。printk()需要特别处理一下 fs 寄存器,它是专用于用户模式的段寄存器。看一看 printk 的代码(在 kernel/printk.c 中)就知道了:int printk(const char *fmt, .) _asm_(“pu

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号