由于一时疏忽,导致who.c的代码不能通过部分测试数据,现已经改正。
今天整理了一下实验报告
在unistd.h中可以看到现在Linux 0.11 支持3个参数的传递。添加参数的方法大概有3条
1.可以采用ESI,EDI,EBP,ESP这几个寄存器传递参数。
2.可以采用《Linux 0.11注释》中提到的系统调用门的办法。
3.可以开辟一块用户态的空间,允许内核态访问,传递参数时,只需传递此空间的首地址指针即可。
向linux 0.11添加一个系统调用foo()的步骤:
首先。在内核中编写系统调用处理函数。
其次。在include/unistd.h中添加系统调用的功能号(#define __NR_foo **)
并且相应的在include/linux/sys.h中声明新的系统调用处理函数以及添加系统
调用处理程序指针数组表中该项的索引值。在make file中添加新系统调用所在
文件的编译、链接规则(依赖关系)。修改system_call.s中系统调用总数。
最后。在应用程序中提供接口,调用系统调用。
这次试验,需要用c来写,比上次要简单一些。
需要改写unistd.h sys.h system_call.s makefile 4个文件
而且需要自己写出 who.c iam.c whoami.c 3个文件
sys.h在 linux-0.11/include/linux 之中,源文件关键处如下
extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
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 };
找到相应的函数名,调用其函数。
extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
extern int sys_iam();
extern int sys_whoami();
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_iam,sys_whoami };
system_call.s 在 linux-0.11/kernel 中
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12
nr_system_calls = 72
只需要把 nr_system_calls 改为 74 就好 ,其代表了中断函数的个数。修改后是这样。
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12
nr_system_calls = 74
要想让我们添加的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 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
Makefile修改后,和往常一样“make all”就能自动把who.c加入到内核中了。如果编译时提示who.c有错误,就说明修改生效了。所以,有意或无意地制造一两个错误也不完全是坏事,至少能证明Makefile是对的。
unistd.h 不能直接在oslab直接直接修改,而需要在虚拟机中修改,在oslab中有一个mount-hdc脚本
运行sudo ./mount-hdc 可以把虚拟机硬盘挂载在oslab/hdc 目录下。
在hdc/usr/include 目录下修改unistd.h
关键原代码为
#define __NR_ssetmask 69
#define __NR_setreuid 70
#define __NR_setregid 71
只需要把两个自定义函数的宏定义在这里就好。
修改后为
#define __NR_ssetmask 69
#define __NR_setreuid 70
#define __NR_setregid 71
#define __NR_iam 72
#define __NR_whoami 73
who.c 的代码如下
#define __LIBRARY__
#include
#include
#include
char temp[64]={0};
int sys_iam(const char* name)
{
int i=0;
while(get_fs_byte(name+i)!='\0')
i++;
if(i>23){
return -EINVAL;
}
printk("%d\n",i);
i=0;
while((temp[i]=get_fs_byte(name+i))!='\0'){
i++;
}
return i;
}
int sys_whoami(char* name,unsigned int size)
{
int i=0;
while (temp[i]!='\0')
i++;
if (size
写完后吧 who.c 放到linux-0.01/kernel 目录下 执行make all who.c 就会被编译到内核里了
iam.c 与 whoami.c 两个文件是测试用的,可以在本地写好,通过挂载的hdc传到linux下,也可以通过vi在linux下写好(如果你蛋疼的话)
一定要在linux 执行编译 ,然后执行sync确保缓冲区数据写入磁盘就可以了。
代码如下
iam.c
#define __LIBRARY__
#include
#include
#include
#include
_syscall1(int,iam,const char*,name)
int main(int argc,char* argv[])
{
if(argc>1){
if(iam(argv[1])<0){
printf("SystemCall Exception!\n");
return -1;
}
}
else{
printf("Input Exception!\n");
return -1;
}
return 0;
}
#define __LIBRARY__
#include
#include
#include
#include
_syscall2(int,whoami,char*,name,unsigned int,size)
int main()
{
int counter;
char buff[128]={0};
counter=whoami(buff,128);
if(counter < 0)
{
printf("SystemCall Exception!");
return -1;
}
else{
printf("%s\n",buff);
}
return 0;
}
最后需要用脚本测试一下,程序的正确性,把脚本放到iam.c whoami.c同目录下 运行就好
脚本代码如下
经过分析它不会检查你的的 erron是不是正确,只会检查whoami返回的的字符串正不正确。
最后一个数据很阴险,注意字符串过长的时候,不要覆盖之前保存的字符串,就是说所保存的字符串是之前存的字符串就好。
#/bin/sh
string1="Sunner"
string2="Richard Stallman"
string3="This is a very very long string!"
score1=10
score2=10
score3=10
expected1="Sunner"
expected2="Richard Stallman"
expected3="Richard Stallman"
echo Testing string:$string1
./iam "$string1"
result=`./whoami`
if [ "$result" = "$expected1" ]; then
echo PASS.
else
score1=0
echo FAILED.
fi
score=$score1
echo Testing string:$string2
./iam "$string2"
result=`./whoami`
if [ "$result" = "$expected2" ]; then
echo PASS.
else
score2=0
echo FAILED.
fi
score=$score+$score2
echo Testing string:$string3
./iam "$string3"
result=`./whoami`
if [ "$result" = "$expected3" ]; then
echo PASS.
else
score3=0
echo FAILED.
fi
score=$score+$score3
let "totalscore=$score"
echo Score: $score = $totalscore%
另外一个测试程序testlab2.c会检查你的返回值是否正确。