系统调用
4.1 实验目的
- 建立对系统调用接口的深入认识jj- 掌握系统调用的基本过程
- 能完成系统调用的全面控制
- 为后续实验做准备
4.2 实验内容
4.2.1 iam()
第一个系统调用是 iam()
,其原型为:
int iam(const char * name);
完成的功能是将字符串参数 name
的内容拷贝到内核中保存下来。 要求 name
的长度不能超过 23
个字符。返回值是拷贝的字符数。 如果 name
的字符个数超过了 23
,则返回 -1
,并置 errno
为 EINVAL
。
在 kernal/who.c
中实现此系统调用。
4.2.2 whoami()
第二个系统调用是 whoami()
,其原型为:
int whoami(char* name, unsigned int size);
它将内核中由 iam()
保存的名字拷贝到 name
指向的用户地址空间中, 同时确保不会对 name
越界访存( name
的大小由 size
说明)。 返回值是拷贝的字符数。如果 size
小于需要的空间,则返回 -1
,并置 errno
为 EINVAL
。
也是在kernel/who.c
中实现
首先先实现who.c的系统调用方法:实现 iam
和 whoami
iam()
实现
int sys_iam(const char* name){
int count =0;
errno = 0;
char temp;
char temp_msg[24];//创造的保存的缓冲
while((temp = get_fs_byte(name+count))!= '\0')
{
if(count >= 23){
errno = EINVAL;
return -(EINVAL);
}
temp_msg[count++]=temp;
}
memset(msg,'\0',sizeof(msg));
memcpy(msg,temp_msg,count);
return (errno == EINVAL)? -(EINVAL):count;
}
whoami()
实现
int sys_whoami(char* pos,unsigned int size)
{
errno = 0;
int length = strlen(msg);
if(length > size){
errno = EINVAL;
return -(EINVAL);
}
for(int i=0;i<length;i++){
put_fs_byte(msg[i],pos+i);
}
return length;
}
完整who.c实现代码:
#include <errno.h>
#include <asm/segment.h>
#include <string.h>
char msg[24]; //内核区保存所输入的字符串的全局变量
int sys_iam(const char* name){
int count =0;
errno = 0;
char temp;
char temp_msg[24];//创造的保存的缓冲
while((temp = get_fs_byte(name+count))!= '\0')
{
if(count >= 23){
errno = EINVAL;
//如果长度过长就返回错误值
return -(EINVAL);
}
temp_msg[count++]=temp;
}
//如果没有出现大小问题,就将这个字符串拷贝到内存中
memset(msg,'\0',sizeof(msg)); //在内存中开辟空间
memcpy(msg,temp_msg,count); //赋值
return (errno == EINVAL)? -(EINVAL):count;
}
int sys_whoami(char* pos,unsigned int size)
{
errno = 0;
int length = strlen(msg);
if(length > size){
errno = EINVAL;
return -(EINVAL);//如果出现了大小不合适的情况,就返回错误值
}
for(int i=0;i<length;i++){
put_fs_byte(msg[i],pos+i); //将数据从内存中读出
}
return length;
}
此时的关键函数是get_fs_byte()
和 get_fs_word()
两个函数进行访问。这样内核就可以将数据传输到用户空间,并可以从用户空间写入内核数据。
修改Makefile文件:
分别在最终的OBJS
的目标中添加了who.o
文件,同时为了生成依赖文件,将who.c
进行编译
然后再unistd.h
中增添系统调用,需要添加调用的功能号和函数原型定义,如下图所示:
添加完成之后再修改sys.h
中的外部函数声明以及修改函数的指针表如下所示
最后修改对应的system_call.s文件,这样可以保证系统调用who.c的两个函数的可用性
这样返回linux-0.11
重新编译执行就可以继续运行了
4.2.3 测试程序
需要分别创建iam.c
和whoami.c
来编译执行
//iam.c
#define __LIBRARY__
#include <unistd.h>
_syscall1(int,iam,const char *, name);
int main(int argc,char* argv[])
{
iam(argv[1]);
return 0;
}
//whoami.c
#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>
#include <string.h>
_syscall2(int,whoami,char*,pos,unsigned int,size);
int main(int argc,char* argv[])
{
char tempstr[24];
memset(tempstr,'\0',sizeof(tempstr));
whoami(tempstr,24);
printf("%s\n",tempstr);
return 0;
}
然后使用命令:
cd ~/oslab
sudo ./mount-hdc
cp ./iam.c hdc/usr/root
cp ./whoami.c hdc/usr/root
cp files/testlab2.c hdc/usr/root
cp files/testlab.sh hdc/usr/root
sudo umount hdc
这样就将所有的测试文件都放入了内核的/usr/root的文件夹,然后修改一下/usr/include/unistd.h文件夹里的内容,变化的方式如下,和编译系统内核的时候做的事情一样就可以:
![image-20231117225829553](https://img-blog.csdnimg.cn/img_convert/d46c995c2bd82957f89726fb26426d17.png)
使用bochs就可以正常运行了
编译当前的两个调用系统函数的代码
然后先进行简单的测试
接着编译测试的testlab2.c
程序进行测试
可以看到测试全部通过
接着使用脚本文件testlab2.sh
进行测试,主要测试的是当前生成的iam
和whoami
的两个可执行文件
可以看到测试全部通过
4.3 实验报告
从 Linux 0.11
现在的机制看,它的系统调用最多能传递几个参数?
最多是3个,可以从linux-0.11
文件里面观察得出,因为系统调用函数的定义一共就只有四种,分别是_syscall0
/_syscall1
/_syscall2
/_syscall3
/三种调用的函数,一共对应分别参数是0,1,2,3;
从另一个角度来看,程序只使用三个通用寄存器ebx
/ecx
/edx
来存放参数,所以决定了整个系统调用只能传输三个参数,
你能想出方法来扩大这个限制吗?
可以使用指针传递的方式传递多个参数,比如将需要的参数转化成数组或者将参数变化成结构体的方式,这样就可以在传递参数的时候给出首地址的位置,就能够隐式的传递多个参数,这样就可以完成合理的扩大限制;或者在硬件上来对传递参数的寄存器个数来进行修改,比如增加更多的传递参数的寄存器,来起到同时传递多个参数的作用,这样就能在根本上完成系统调用使用的参数的个数的扩大操作。
用文字简要描述向 Linux 0.11
添加一个系统调用 foo()
的步骤。
在Linux 0.11 中添加一个系统调用foo()
的步骤如下:
首先先实现函数foo()
确定输入和输出的参数,确定在系统内核中的可实现性;然后修改编译的文件,将这个的可编译的程序片段放入系统调用使用的目标文件里面,在本实验中指代的是OBJS
,修改完成并编译之后修改对应的头文件的内容,可以显式的告知内核程序新增了的系统调用,最后更改函数的指针表,这样就可以正常的找到系统调用程序并进行正常的系统调用。重新编译linux-0.11内核就完成了添加一个系统调用foo()
的操作。