这是我的第一篇博客!😃,终于鼓起勇气写博客了(/不好意思 冷汗 尴尬),希望这是一个好的开始,能将博客坚持写下去。对于博客中有误的地方,欢迎大家指正。
由于对Markdown编辑器不熟练,博客的界面似乎写的有点糟糕 😦
实验内容
此次实验的基本内容是:在Linux 0.11上添加两个系统调用,并编写两个简单的应用程序测试它们。
说明;
第一个系统调用是iam(),其原型为:int iam(const char * name);
完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。在kernal/who.c中实现此系统调用。
第二个系统调用是whoami(),其原型为:int whoami(char* name, unsigned int size);
它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。也是在kernal/who.c中实现。
实验过程
(1)首先将外层用户程序whoami(),和iam()实现,在 oslab/linux-0.11/kernel(这个路径可以随意)下建立了两个文件whoami.c和iam.c。
whoami.c:
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define SIZE 64
char name[SIZE] = {'\0'};
_syscall2(int,whoami,char*,name,unsigned int,size)/*这个宏展开后就是whoiam(ccahr*, int)*{...}*/
int main(int argc,char* argv[]){
int len = 0;
len = whoami(name,SIZE);
if(len<0){
printf("SystemCall Exception!");
return 0;
}
else
printf("%s\n",name);
return 0;
}
iam.c:
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
char name[64] = {'\0'};
_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;
}
printf("Success!\n");
}
else {
printf("Input Exception!\n");
return -1;
}
return 0;
}
这两段程序较为简单,但需要注意的是#define LIBRARY;#include <unistd.h>;和系统调用(_syscall)三者的顺序。程序中 name[SIZE] 是用户空间的数据。
(2)每个系统调用都有一个sys_xxxxxx()与之对应。在 oslab/linux-0.11/kernel 下新建who.c文件 ,在内核中实现函数sys_iam()和sys_whoami()。
who.c:
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
#include <asm/segment.h>
char myName[24] = {"\0"};
int sys_iam(const char *name){
int i = 0;
printk("entry kernel!\n");
while(get_fs_byte(name+i) != '\0'){
i++;
printk(".");
if(i>23) {
printk("paramater is error!\n");
return (-EINVAL);
}
}
i = 0;
while((myName[i] = get_fs_byte(name+i)) != '\0')
i++;
printk("exit!\n");
return i;
}
int sys_whoami(char *name,int size){
int i = 0;
while(myName[i] != '\0'){
i++;
}
if(size<i) return (-EINVAL);
i = 0;
while(myName[i] != '\0'){
put_fs_byte(myName[i],name+(i++));
}
*(name+i) = '\0';
return i;
}
为了方便调试,在函数中加入了printk(),用于打印调试信息。程序中 myName[24] 属于内核空间。关于Linux0.11内核空间、用户空间之间的数据传输可以参考这篇博客:https://blog.csdn.net/yming0221/article/details/6269359 。
(3)在程序编写完成之后就需要根据系统调用的过程进行相关文件的修改了。
1、在hdc/usr/include 目录下修改unistd.h。 注意:unistd.h 不能在oslab中直接修改,而需要在虚拟机中(oslab/hdc/usr/include )修改(具体原因我还不太清楚),在oslab目录下有一个mount-hdc脚本。运行sudo ./mount-hdc 可以把虚拟机硬盘挂载在oslab/hdc 目录下。
#define __NR_sigaction 67
#define __NR_sgetmask 68
#define __NR_ssetmask 69
#define __NR_setreuid 70
#define __NR_setregid 71
//在这里添加两个系统调用号
#define __NR_iam 72
#define __NR_whoami 73
2、 在 oslab/linux-0.11/kernel 目录下修改system_call.s。nr_system_calls表示系统调用的总数
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12
# nr_system_calls 的值改为74
nr_system_calls = 74
3、在 oslab/linux-0.11/include/linux 目录下修改sys.h。
extern int sys_setreuid();
extern int sys_setregid();
//在这里添加两个系统函数的声明
extern int sys_iam();
extern int sys_whoami();
//在系统调用函数表sys_call_table[]末尾增加两个函数名:sys_iam,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 };
4、在 oslab/linux-0.11/kernel 目录下修改MakeFile文件,Makefile修改后,和往常一样执行“make”就能自动把who.c加入到内核中了。
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
5、将iam.c和whoami.c复制到 hdc/usr/usr/root 目录下。(sudo ./mount-hdc 可以把虚拟机硬盘挂载在oslab/hdc 目录)注意要在模拟器Bochs启动之后的Linux0.11环境中编译iam.c和whoami.c,否则会报错。
6、./run 启动Linux0.11,编译am.c和whoami.c,若无警告信息则编译通过了。
实验结果
参考博客
https://blog.csdn.net/wangyi_lin/article/details/6921110
https://blog.csdn.net/u013129143/article/details/82934335
https://blog.csdn.net/yming0221/article/details/6269359