HIT oslab之实验3 系统调用(sys_iam + sys_whoami)

一、实验内容

1.在原有的Linux 0.11 上添加两个系统调用;

注意 : 实验2 是重写bootsect.s 和 setup.s;
通过VMware快照功能,恢复到原有的Linux-0.11开始实验3。

(1)iam()

int iam(const char * name);

完成的功能:将字符串参数 name 的内容拷贝到内核中保存下来。
要求:name 的长度不能超过 23 个字符。
返回值:拷贝的字符数。如果 name 的字符个数超过了 23,则返回 “-1”,并置 errno 为 EINVAL(即:return -EINVAL)。

(2)whoami()

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

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

2.编写两个简单的应用程序测试它们;

二、实验过程

1. 系统调用的整体逻辑

在这里插入图片描述

本实验采用,在用户程序中直接执行对应的系统调用。

(1)从用户级转到内核级的唯一方式:中断。
在这里插入图片描述

这是iam.c的部分代码截图,解释:
①_syscall1是宏函数,详细了解见《Linux-0.11内核完全注释》5.5和参考资料;该函数主要实现2点:eax = 系统调用号 + int 0x80
在这里插入图片描述

(2)system_call函数
在这里插入图片描述
(3)系统调用函数:sys_xxx

本次实验要编写2个系统调用函数,即①sys_iam;②sys_whoami; 见“二、2.”

2. 按照上述逻辑修改相应文件

(1) 修改系统调用总数, 文件:kernel/system_call.s
在这里插入图片描述在这里插入图片描述
(2)在系统调用函数表中增加系统调用条目,文件:include/linux/sys.h
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

千万别异想天开的放在最前面,不然之后添加_NR_xxxxxx会很麻烦,微笑脸。

(3)添加iam和whoami系统调用编号的宏定义(_NR_xxxxxx),文件:include/unistd.h
在这里插入图片描述
在这里插入图片描述

系统调用函数在 sys_call_table 数组中的位置必须和 __NR_xxxxxx 的值对应上。上文提到的 eax 中放的是系统调用号,即 __NR_xxxxxx。

(4)验证上述修改是否成功,先简单实现sys_iam和sys_whoami,文件:kernel/who.c
在这里插入图片描述
在这里插入图片描述
(5)修改Makefile 文件:kernel/Makefile
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

插播:之后调试会用到printk函数,它也是在kernel目录下,而且在Makefile中,有…/include/linux/kernel.h,所以可以使用printk函数。

在这里插入图片描述
(6)进入linux-0.11,编写应用程序进行测试
vi /usr/include/unistd.h 增加iam和whoami的宏定义
在这里插入图片描述
在这里插入图片描述

别忘了经常执行“sync”以确保内存缓冲区的数据写入磁盘,不然的话重新进入linux-0.11得重新增加宏定义。

②编写测试文件:test.c

#define __LIBRARY__   /* 必须定义这个宏 */
#include <unistd.h>

/* iam 和 whoami 系统调用的用户接口 */
_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int, size);

int main(void)
{
    char buf[24];
    iam("test");
    whoami(buf, 24);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(7)总结:至此,说明成功添加系统调用了。

测试参考教程

2.实现sys_iam和sys_whoami

(1)代码

#include <string.h> //实现置 errno为EINVAL
#include <errno.h> //调用了strcpy
#include <asm/segment.h> //调用了get_fs_byte, put_fs_byte

#definie maxSize 24
char msg[maxSize]; //这样就可以在内核中保存下来,最后一位是'\0'

int sys_iam(const char *name)
{
	char tmp[maxSize];
	int i;
	for(i = 0; i < maxSize; i++) {
		tmp[i] = get_fs_byte(name + i);
		//printk("%c\n", tmp[i]);
		if(tmp[i] == '\0') break;  //'\0'表示字符串结束了
	}
	if(i == maxSize) {
		//printk("too long!\n");
		return -EINVAL;
	}
	else {
		strcpy(msg, tmp);  //感觉在内核中调用C语言库会不太好
		return i;
	}
}

int sys_whoami(char *name, unsigned int size)
{
	int msg_size = 0;
	while(msg[msg_size] != '\0') msg_size++;
	//printk("msg_size : %d\n", msg_size);
	//printk("msg : %s\n", msg);
	if(size < msg_size) return -EINVAL;
	else {
		
		int i;
		//printk("size : %d\n", size);
		for(i = 0; i < size; i++) {
			//printk("ok\n");
			//printk("name : %c\n", name[i]);
			//printk("msg : %c\n", msg[i]);
			put_fs_byte(msg[i], name + i);
			if(msg[i] == '\0') break;
		}
		return i;
	}
}

适当地向屏幕输出一些程序运行状态的信息,也是一种很高效、便捷的调试方法,有时甚至是唯一的方法,被称为“printf 法”。
for(int i = 0; i < size; i++)这么写会报错,解释

3.编写两个简单的应用程序进行测试

(1)iam.c代码

#include <unistd.h>  //有它,编译器才能获知自定义的系统调用的编号

#define __LIBRARY__  //有它,_syscalln才有效

_syscall1(int,iam,const char*,name);

int main(int argc, char **argv) {
	iam(argv[1]);
	return 0;
}

在这里插入图片描述
(2)whoami.c代码

#include <unistd.h>  //有它,编译器才能获知自定义的系统调用的编号
#include <stdio.h>
#define __LIBRARY__  //有它,_syscalln才有效

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

int main() {
	char s[30];
	whoami(s, 30);
	printf("%s\n", s);
	return 0;
}

在这里插入图片描述

这里有些心酸的事儿:上古时代linux-0.11自带的vi编辑器和今天的语法规则不一样吧,或者是键盘的原因。总之,输入很不方便。由此导致了一些莫名其妙的问题,只能重新开始写,认认真真保证一次不错的敲好,才幸运的编译通过…或者,宏函数的参数不能"int, whoami"多了个空格,必须要"int,whoami"?

可以采用Ubuntu和Linux-0.11文件交换的方式,在oslab目录下,输入命令sudo ./mount-hdc

(3)验证
在这里插入图片描述

为了得到正确结果,对who.c调试的时候,也发生了莫名其妙的bug,增加了一些printk,其他也没改,又莫名其妙的好…玄学!总之,可算是看到上图的结果了。

三、其他

实验过程中的一些小积累。

1.对const char *的理解。(以const char * ptr为例子)
ptr是指向字符串变量的指针,可以指向不同的字符串,但不能修改指向字符串的内容。
在这里插入图片描述

#include <cstdio>

int main() {
    const char * ccptr;
    char * cptr;
    char name1[] = {"Hello World!"};
    char name2[] = {"World Hello!"};
    ccptr = name1; 
    cptr = name1; 
    
    //ccptr[0] = 'h';
    cptr[0] = 'h';
    printf("name1 : %s\n", ccptr);
    ccptr = name2;
    printf("name2 : %s\n", ccptr);

    return 0;
}

在这里插入图片描述

总结 : const char * ptr;
①不可以通过ptr[i]来改变指向的字符串;
②但可以指向其他的字符串。

2.C语言级调试遇到的问题及解决办法
oslab目录下,通过以下命令进行C语言级调试:
./dbg-asm
./rungdb

新开一个终端窗口,进入oslab目录

(1)问题1:缺少libncurses.so.5
在这里插入图片描述
解决办法:
sudo add-apt-repository universe
sudo apt-get install libncurses5 libncurses5:i386
在这里插入图片描述

参考资料

(2)问题2:缺少libexpat.so.1
在这里插入图片描述
解决办法:
在这里插入图片描述
在这里插入图片描述

解决这个问题,兜了些圈子,不敢保证一定有效。
参考资料

3.sys_iam中,遇到一个疑惑,读取到的name,怎么保存到内核中,使得whoiam去读取。
解决办法:用全局字符数组就行!
在这里插入图片描述

唉,傻到了啊

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值