系统调用

本文详细介绍了在Linux0.11环境中实现和测试两个系统调用函数iam()和whoami()的过程,包括原型定义、内核实现、测试程序及其实验报告。同时讨论了系统调用参数限制的扩展可能性。
摘要由CSDN通过智能技术生成

系统调用

4.1 实验目的

  • 建立对系统调用接口的深入认识jj- 掌握系统调用的基本过程
  • 能完成系统调用的全面控制
  • 为后续实验做准备

4.2 实验内容

4.2.1 iam()

第一个系统调用是 iam() ,其原型为:

int iam(const char * name);

完成的功能是将字符串参数 name 的内容拷贝到内核中保存下来。 要求 name 的长度不能超过 23 个字符。返回值是拷贝的字符数。 如果 name 的字符个数超过了 23 ,则返回 -1 ,并置 errnoEINVAL

kernal/who.c 中实现此系统调用。

4.2.2 whoami()

第二个系统调用是 whoami() ,其原型为:

int whoami(char* name, unsigned int size);

它将内核中由 iam() 保存的名字拷贝到 name 指向的用户地址空间中, 同时确保不会对 name 越界访存( name 的大小由 size 说明)。 返回值是拷贝的字符数。如果 size 小于需要的空间,则返回 -1 ,并置 errnoEINVAL

也是在kernel/who.c 中实现

首先先实现who.c的系统调用方法:实现 iamwhoami

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中增添系统调用,需要添加调用的功能号和函数原型定义,如下图所示:

image-20231117224628858

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加完成之后再修改sys.h中的外部函数声明以及修改函数的指针表如下所示

image-20231117224809801

最后修改对应的system_call.s文件,这样可以保证系统调用who.c的两个函数的可用性

image-20231117225007027

这样返回linux-0.11重新编译执行就可以继续运行了

4.2.3 测试程序

需要分别创建iam.cwhoami.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

使用bochs就可以正常运行了

image-20231117230143525

编译当前的两个调用系统函数的代码

然后先进行简单的测试

image-20231117230255805

接着编译测试的testlab2.c程序进行测试

image-20231117230348317

可以看到测试全部通过

接着使用脚本文件testlab2.sh进行测试,主要测试的是当前生成的iamwhoami的两个可执行文件

image-20231117230519473

可以看到测试全部通过

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()的操作。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值