HIT软件学院第二次OS实验

来源:互联网 发布:网络对于大学生的影响 编辑:程序博客网 时间:2024/05/29 04:31

这次实验是让你在linux0.11上增加两个系统调用,并编写两个简单的应用程序测试你写的系统调用。

iam()

第一个系统调用是iam(),其原型为:

int iam(const char * name);

完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。

在kernel/who.c中实现此系统调用。

whoami()

第二个系统调用是whoami(),其原型为:

int whoami(char* name, unsigned int size);

它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。

也是在kernel/who.c中实现。

要注意的是这两个函数是系统调用,是内核态的。

测试程序

运行添加过新系统调用的Linux 0.11,在其环境下编写两个测试程序iam.c和whoami.c。最终的运行结果是:

$ ./iam lizhijun$ ./whoamilizhijun 

这个测试程序是一个普通的用户态的C语言程序,你需要在这个函数中调用你上面你编写的那两个系统调用。

这次实验首先应该大致了解一下linux0.11系统调用的过程,仔细阅读实验指导书,然后看看现成的系统调用(如lib/close.c中的系统调用),之后再研习一下赵烔博士的《linux0.11内核完全注释》相应的章节会对这次实验会对这次实验有很大帮助。

你首先需要修改/include/unistd.h,在里面加上新系统调用的宏定义,代码如下:

#define __NR_sgetmask68#define __NR_ssetmask69#define __NR_setreuid70#define __NR_setregid71     /*系统原来的系统调用,可见原来linux0.11中有72个系统调用(从0开始)*/#define __NR_whoami72     /*新系统调用的宏定义*/#define __NR_iam73     /*新系统调用的宏定义*/

然后你要修改/include/linux/sys.h,学着前面的方式在里面加上extern int sys_whoami()和extern int sys_iam(),

extern int sys_sgetmask();extern int sys_ssetmask();extern int sys_setreuid();extern int sys_setregid();extern int sys_whoami();     /*你添加的已存在声明*/extern int sys_iam();     /*你添加的已存在声明*/

并在该文件的sys_call_table[]数组中加上两项sys_whoami和sys_iam(这里sys_whoami和sys_iam在该数组的位置一定要和上述宏定义的数字相对应,比如sys_whoami在/include/unistd.h中的宏定义为72,则sys_whoami应该是该数组的第72项,从0开始数),如下所示:

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,sys_setreuid,sys_setregid, sys_whoami, sys_iam };     /*在后面添加两项*/

然后,修改/kernel里面的system_call.s文件改变里面的系统调用个数,也即在原来的个数上加你添加的系统调用数,如下所示:

# offsets within sigactionsa_handler = 0sa_mask = 4sa_flags = 8sa_restorer = 12nr_system_calls = 74  /*原来是72,你加了两个,变成74*//* * Ok, I get parallel printer interrupts while using the floppy for some * strange reason. Urgel. Now I just ignore them. */

最后在/kernel中编写实现新加的系统调用函数的文件who.c,实现你新加的系统调用:

#include <errno.h>#include <fcntl.h>#include <sys/types.h>#include <utime.h>#include <sys/stat.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/kernel.h>#include <asm/segment.h>#include <string.h>/*以上头文件可能不必全包含,但是我偷懒了,直接全包含算了*//*注意:注释时一定不要用双斜线,变量的声明要全写在可执行语句的前面,必须严格按着C语言的语法*/char myName[23];/*内核态全局变量,保存从用户态得到的名字*/int len;/*内核态全局变量,保存从用户态得到的名字的长度*//*注意:在本文件中声明以及定义的变量均为内核态变量,而且不能直接访问用户态变量*//*2011-11-4 19:23*/int sys_iam(const char *name){char temp[23];int i;for (i=0;(temp[i] = get_fs_byte(&name[i]))!='\0';i++){}/*获取名字的长度*/if (i>23){return (-EINVAL);/*直接这样写可以把指导书上的情况全包含*/}len = i;for (i=0;i<len;i++){myName[i] = temp[i];}/*printk("Save complete!\n");*//*内核中向终端输出用printk函数*/return len;}int sys_whoami(char* name, unsigned int size){int i;if (len>size){return (-EINVAL);}for (i=0;i<len;i++){put_fs_byte(myName[i],&name[i]);}/*printk("%s\n",myName);*//*printk("Copy complete!\n");*/return len;}

到这里系统调用就添加完成了,接下来修改kernel目录下的makefile文件,让我们添加的kernel/who.c可以和其它Linux代码编译链接到一起,如下:

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改为: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 \  ../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.h改为:### Dependencies:who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.hexit.s exit.o: exit.c ../include/errno.h ../include/signal.h \  ../include/sys/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.h

接下来编写测试程序,在虚拟机上运行,测试你的系统调用是否添加成功:

iam.c样例代码:

#define __LIBRARY__/* 有它,_syscall1等才有效。详见unistd.h */#include <unistd.h>/* 有它,编译器才能获知自定义的系统调用的编号 */#include <stdlib.h>#include <stdio.h>#include <string.h>_syscall1(int, iam, const char*, name);/* iam()在用户空间的接口函数 *//*2011-11-4 19:24*/void main(int arg ,char *argv[]){/*printf("%s\n",argv[0]);printf("%s\n",argv[1]);*/iam(argv[1]);}

whoami.c 样例代码:

#define __LIBRARY__/* 有它,_syscall1等才有效。详见unistd.h */#include <unistd.h>/* 有它,编译器才能获知自定义的系统调用的编号 */#include <stdlib.h>#include <stdio.h>_syscall2(int, whoami,char*,name,unsigned int,size);/* whoami()在用户空间的接口函数 *//*2011-11-4 19:25*/void main(int arg ,char *argv[]){char *aname;int alen;/*unsigned int n = 0;*//*printf("%s\n",argv[0]);*//*printf("%s\n",argv[1]);*//*scanf("%d",&n);*/aname = (char *)malloc(sizeof(char)*23);alen = whoami(aname,23);printf("%s\n",aname);/*printf("%d\n",alen);*/}


实验报告参考:

/*1.从Linux 0.11现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?答:当应用程序经过库函数向内核发送一个中断调用int 0x80,开始执行一个系统调用。其中寄存器eax存放着系统调用号,参数可以存放寄存器ebx、ecx、edx中。因此Linux 0.11系统调用最多能向内核传递3个参数。解决方案是传给函数一个指针值,该指针值指向一个大块数据,这个大块数据可以是一个线性表或者某个特定的数据结构,存放着该函数所有的参数,这样通过寄存器间接寻址,便可向系统调用传递多个参数。2.用文字简要描述向Linux 0.11添加一个系统调用foo()的步骤。答:首先修改/include/unistd.h,在里面加上新系统调用的宏定义:#define __NR_foo72(原来从0开始数有71个系统调用,加了一个,变成72)。然后修改/include/linux/sys.h,在里面加上extern int sys_foo();,并在该文件的fn_ptr sys_call_table[]数组中加上一项sys_foo(这里sys_foo在该数组的位置一定要和上述宏定义的数字相对应,比如上述为72,则sys_foo应该是该数组的第72项,从0开始数)。然后修改/kernel里面的system_call.s文件改变里面的系统调用个数,也即在原来的个数上加1;最后在/kernel中编写实现新加的系统调用函数的文件foo.c,实现你新加的函数,到这里系统调用就添加完成了。最后修改makefile文件,使其在编译linux时能将新加的调用同时编译,全部完成(还可以编写测试程序测试新加的系统调用是否成功)。*/



还可以下载测试脚本测试你得多少分,一些具体的细节请看实验指导书,会对你有很大帮助,加油吧!!