博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
操作系统实验报告-系统调用
阅读量:5157 次
发布时间:2019-06-13

本文共 5010 字,大约阅读时间需要 16 分钟。

实验内容

在Linux 0.11上添加两个系统调用,并编写两个简单的应用程序测试它们。

iam()

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

int iam(const char * name);

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

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

whoami()

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

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

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

也是在kernal/who.c中实现。

实验步骤

代码的修改和补充

include/linux/sys.h:

添加声明:

extern int sys_iam();extern int sys_whoam();

在sys_call_table数组的最后增加两个元素:

..., sys_iam, sys_whoami}

include/unistd.h:

定义调用号宏:

#define __NR_iam    72#define __NR_whoami    73

声明供用户调用的函数:

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

kernel/system_call.s:

修改调用个数(从72改为74,调用号从0开始计数):

nr_system_calls = 74

+kernel/who.c:

添加此文件,实现系统调用:

#define __LIBRARY__#include 
#include
#include
#define MAX_SIZE 23char username[MAX_SIZE+1];int sys_iam(const char *name){ int i; for(i=0; get_fs_byte(name+i)!='\0'; i++);      // 注解1:get_fs_byte() printk("sys_iam:\n\t name size is:%d \n", i); if(i>MAX_SIZE) return -EINVAL; for(i=0; (username[i]=get_fs_byte(name+i))!='\0'; i++); return i; } int sys_whoami(char *name, unsigned int size) { int i; for(i=0; put_fs_byte(username[i], name+i), username[i]!='\0'; i++); printk("sys_whoami:\n\t username size is:%d \n\t given size is:%d\n", i, size); if(i>size) return -EINVAL; return i; }

 

注解1:

// 由于sys_iam()的参数name指针来自用户空间,sys_iam()运行处于内核态,默认使用的是ds、es段寄存器,  // 注解1.1:发生系统调用时段寄存器的设置 // 要取得用户空间的数据需要临时改变为fs段寄存器(用于用户空间)// segment.h中的get_fs_byte()static inline unsigned char get_fs_byte(const char * addr){     unsigned register char _v;    __asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));  # => movb *fs:addr, _v 将用户空间addr处的数据存入_v寄存器    return _v;}
注解1.1:
system_call:    cmpl $nr_system_calls-1,%eax  # 系统调用时会将调用号存入eax,将其与最大的系统调用号进行比较    ja bad_sys_call          # 如果超过,则跳转到bad_sys_call(将-1存入eax并返回)    push %ds      # 压栈    push %es    push %fs pushl %edx      # 系统调用通过这3个寄存器传参,压栈作为参数 pushl %ecx  pushl %ebx movl $0x10,%edx # 这3行将ds、es设置为内核口空间段地址0x10 mov %dx,%ds mov %dx,%es movl $0x17,%edx # 这2行将fs设置为用户空间段地址0x17 mov %dx,%fs call sys_call_table(,%eax,4)  # 调用sys_call_table+eax*4处的函数                       # sys_call_table是include/linux/sys.h中的函数入口数组,                       # 它的类型是fn_ptr,定义在include/linux/sched.h中,                       # typedef int (*fn_ptr)(); 实际是将一个int类型定义为函数指针                       # 所以这里根据系统调用号计算调用入口地址时需要*4(int为4个byte) pushl %eax movl current,%eax cmpl $0,state(%eax)        # state和下面的counter分别被初始化为0和4,作为current所属结构体相应数据偏移 jne reschedule            # 根据比较结果判断是否重新调度 cmpl $0,counter(%eax) je reschedule

 

kernel/Makefile:

修改Makefile,补充与who.c相关规则:

OBJS  = ... who.o......### Dependencies:who.s who.o: who.c ../include/unistd.h \  ../include/asm/segment.h ../include/errno.h......

测试

添加测试用例

运行oslab目录下的mount-hdc,挂载虚拟硬盘:

cd ~/workspace/oslabsudo ./mount-hdc cd hdc/usr/root

添加测试文件:

sudo vim iam.c

iam.c具体内容:

#include 
#define __LIBRARY__  # 在unistd.h中,调用号与_syscall*宏函数都是在定义了__LIBRARY__的前提下才定义的#include
_syscall1(int,iam,const char *,name)  # 注解2:_syscall*()int main(int argc, char *argv[]){ if(argc<=1) { printf("error: input your name, please! \n "); return -1; } if(iam(argv[1])==-1) { printf("error: length limit is 23. \n"); return -1; } return 0; }
sudo vim whoami.c
#include 
#define __LIBRARY__#include
_syscall2(int,whoami,char *,name,unsigned int,size)int main(int argc, char *argv[]){ char name[24]; if(whoami(name, 23)==-1) { printf("error while reading name..."); return -1; } printf("%s\n", name); return 0; }

实验也给我们提供了测试评分文件:  ,也将它们下载放到当前目录下。

替换部分库文件:

由于挂载的虚拟硬盘为oslab目录下的hdc-0.11.img,里面的文件不会随着内核的编译而改变,我们手动替换改动的库文件:

sudo cp ~/workspace/oslab/linux-0.11/include/linux/sys.h ~/workspace/oslab/hdc/usr/include/linux/sys.h sudo cp ~/workspace/oslab/linux-0.11/include/unistd.h ~/workspace/oslab/hdc/usr/include/unistd.h

OK,我们现在可以卸载hdc了:

cd ~/workspace/oslabsudo umount hdc

编译与运行

cd linux-0.11make ../run

开启时默认的目录即为/usr/root。

编译C文件:

gcc -o iam iam.cgcc -o whoami whoami.cgcc -o testlab2 testlab2.c sync

在bochs上的linux-0.11中产生或修改的文件需要手动写入硬盘,否则关闭虚拟机后不会保存。

运行自己的测试用例:

./iam "lg's student"./whoami

运行实验给的评分用例(满分分别为50%和30%,还有20%是写实验报告。。。):

./testlab2

./testlab2.sh

系统调用的分析

初始化

调用流程

以iam()函数为例:

思考

从Linux 0.11现在的机制看,它的系统调用最多能传递几个参数?你能想出办法来扩大这个限制吗?

Linux-0.11的系统调用通过寄存器ebx、ecx、edx传递参数,最多能传递3个参数。

扩大传递参数的数量的方法:

  1. 增加传参所用的寄存器,但考虑到寄存器的成本以及它们还需要用于其他地方,这个方法不太可取;
  2. 通过定义结构体,在结构体中存入很多参数,然而只把结构体入口地址作为参数进行传递;
  3. 用这3个寄存器循环传值;
  4. 将寄存器拆分为高位和低位传递一直比较小的参数;
  5. 利用堆栈传递参数。

用文字简要描述向Linux 0.11添加一个系统调用foo()的步骤。

我们假设foo()的返回值类型为void。

  1. 在include/unistd.h中定义宏__NR_foo,并添加供用户调用的函数声明void foo();
  2. 修改kernel/system_call.s中的nr_system_calls,使其增加1;
  3. 在include/linux/sys.h中添加函数声明extern void sys_foo(),在系统调用入口表fn_ptr末端添加元素sys_foo;
  4. 添加kernel/foo.c文件,实现sys_foo()函数;
  5. 修改kernel/Makefile,在OBJS中加入foo.o,并添加生成foo.s、foo.o的依赖规则。

转载于:https://www.cnblogs.com/tradoff/p/5734582.html

你可能感兴趣的文章
Python核心编程——多线程threading和队列
查看>>
Program exited with code **** 相关解释
查看>>
植物大战僵尸中文年度版
查看>>
26、linux 几个C函数,nanosleep,lstat,unlink
查看>>
投标项目的脚本练习2
查看>>
201521123107 《Java程序设计》第9周学习总结
查看>>
Caroline--chochukmo
查看>>
iOS之文本属性Attributes的使用
查看>>
从.Net版本演变看String和StringBuilder性能之争
查看>>
Excel操作 Microsoft.Office.Interop.Excel.dll的使用
查看>>
解决Ubuntu下博通网卡驱动问题
查看>>
【bzoj2788】Festival
查看>>
执行gem install dryrun错误
查看>>
Java SE之正则表达式一:概述
查看>>
HTML5简单入门系列(四)
查看>>
实现字符串反转
查看>>
转载:《TypeScript 中文入门教程》 5、命名空间和模块
查看>>
苹果开发中常用英语单词
查看>>
[USACO 1.4.3]等差数列
查看>>
Shader Overview
查看>>