I 实验题目
在linux-0.11上添加两个系统调用并测试
(1)iam()
原型为int iam(const char *name)
将字符串参数name的内容拷贝到内核下保存下来,如果name长度小于等于23,返回拷贝的字符数。如果name的字符个数超过了23,返回-1,置errno为EINVAL。
(2)whoami()
原型为int whoami(char *name, unsigned int size);
iam()保存的名字拷贝到name所指的地址空间。确保不对name越界访问。返回值是拷贝的字符数,如果size小于需要的空间,则返回“-1”,置errno为EINVAL。
II 操作步骤
1.include/unistd.h中添加系统调用号
//include/unistd.h
#define __NR_whoami 72
#define __NR_iam 73
2.kernel/system_call.s修改系统总调用数为74
//kernel/system_call.s
nr_system_calls = 74
3.include/linux/sys.h添加调用
//include/linux/sys.h
extern int sys_whoami();
extern int sys_iam();
fn_ptr_sys_call_table[]={...sys_whoami,sys_iam}
4.编写kernel/who.c
//kernel/who.c
#include <linux/kernel.h>
#include <asm/segment.h>
#include <unistd.h>
#include <errno.h>
int wholen = 0;
char whobuf[30];
int sys_iam(const char* name){
int len; //计算输入字符串长度
for(len=0; len<24; len++){
char tmp = get_fs_byte(name+len);
if(tmp == 0)
break;
}
//如果字符串长度大于23,直接返回-1,并设置errno
if(len == 24){
/*
设置errno这一步卡了我好久。
一开始我是直接赋值给errno,例如:errno = EINVAL; return -1。结果一直过不了。
后面看到别人是直接返回-EINVAL,改了之后就可以了。
*/
return -EINVAL;
}
//拷贝到内核准备好的缓冲区
for(wholen = 0; wholen<=len; wholen++){
whobuf[wholen] = get_fs_byte(name+wholen);
}
return wholen;
}
int sys_whoami(char* name, unsigned int size){
//判断是否有足够的空间存储
if(size < wholen){
return -EINVAL;
}
//拷贝
int i;
for(i=0; i<=wholen; i++){
put_fs_byte(whobuf[i], name+i);
}
return wholen;
}
5.修改kernel/Makefile
//kernel/Makefile
OBJS=...who.o
#Dependencies:
who.s who.o:who.c ../include/linux/kernel.h ../include/unistd.h
6.编写iam.c与whoami.c
#define __LIBRARY__
#include "unistd.h"
_syscall1(int, iam, const char*, name);
int main(int argc, char** argv){
int wlen = 0;
if(argc < 1){
printf("not enougth argument\n");
return -2;
}
wlen = iam(argv[1]);
return wlen;
}
#define __LIBRARY__
#include "unistd.h"
_syscall2(int, whoami,char*,name,unsigned int,size);
int main(int argc, char** argv){
char buf[30];
int rlen;
rlen = whoami(buf, 30);
printf("%s\n", buf);
return rlen;
}
7.命令
~/oslab/linu-0.11
make all
../run
III 测试
1.挂载虚拟硬盘
cd ~/oslab
sudo ./mount-hdc
cd hdc/usr/root
2.在其中添加iam.c whoam.c以及教师文件夹里的testlab2.c testlab2.sh
3.替换部分库文件
sudo cp ~/oslab/linux-0.11/include/linux/sys.h ~/oslab/hdc/usr/include/linux/sys.h
sudo cp ~/oslab/linux-0.11/include/unistd.h ~/oslab/hdc/usr/include/unistd.h
4.卸载hdc
cd ~/oslab
sudo umount hdc
5.编译与运行
cd linux-0.11
make
../run
gcc -o iam iam.c
gcc -o whoami whoami.c
gcc -o testlab2 testlab2.c
sync
./iam "lg's student"
./whoami
./testlab2
IV 实验报告
- 从 Linux 0.11 现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?
Linux-0.11的系统调用通过寄存器ebx、ecx、edx传递参数,最多能传递3个参数。
结构体,堆栈,寄存器拆分为高低位,寄存器循环传值 - 用文字简要描述向 Linux 0.11 添加一个系统调用 foo() 的步骤。
(1)include/unistd.h中添加系统调用号
(2)在kernel/system_call.s修改总调用数
(3)include/linux/sys.h添加调用
(4)在内核文件中实现foo.c
(5)修改kernel/Makefile
V 总结
图片源于https://www.cnblogs.com/tradoff/p/5734582.html
iam()的系统调用流程如上图所示,关键的步骤在于根据include/unistd.h中添加的系统调用号解析系统调用,根据函数调用表在include/linux/sys.h中找到函数入口地址,在kernel中找到函数并实现。
关于中断的陷入,有以下描述:将 system_call 函数地址写到 0x80 对应的中断描述符中,也就是在中断 0x80 发生后,自动调用函数 system_call。