HIT软件学院第二次OS实验

这次实验是让你在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
$ ./whoami
lizhijun 

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

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

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

#define __NR_sgetmask	68
#define __NR_ssetmask	69
#define __NR_setreuid	70
#define __NR_setregid	71     /*系统原来的系统调用,可见原来linux0.11中有72个系统调用(从0开始)*/
#define __NR_whoami	72     /*新系统调用的宏定义*/
#define __NR_iam	73     /*新系统调用的宏定义*/

然后你要修改/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 sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12

nr_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.h
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

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

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_foo	72(原来从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时能将新加的调用同时编译,全部完成(还可以编写测试程序测试新加的系统调用是否成功)。*/



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




你可能感兴趣的:(HIT软件学院第二次OS实验)