操作系统实验报告-系统调用
实验内容
在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 <asm/segment.h> #include <errno.h> #include <unistd.h> #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 %espush %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/oslab sudo ./mount-hdccd hdc/usr/root
添加测试文件:
sudo vim iam.c
iam.c具体内容:
#include <stdio.h> #define __LIBRARY__ # 在unistd.h中,调用号与_syscall*宏函数都是在定义了__LIBRARY__的前提下才定义的 #include <unistd.h> _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 <stdio.h> #define __LIBRARY__ #include <unistd.h> _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; }
实验也给我们提供了测试评分文件:testlab2.c testlab2.sh ,也将它们下载放到当前目录下。
替换部分库文件:
由于挂载的虚拟硬盘为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/oslab sudo umount hdc
编译与运行
cd linux-0.11 make../run
开启时默认的目录即为/usr/root。
编译C文件:
gcc -o iam iam.c gcc -o whoami whoami.c gcc -o testlab2 testlab2.csync
在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个参数。
扩大传递参数的数量的方法:
- 增加传参所用的寄存器,但考虑到寄存器的成本以及它们还需要用于其他地方,这个方法不太可取;
- 通过定义结构体,在结构体中存入很多参数,然而只把结构体入口地址作为参数进行传递;
- 用这3个寄存器循环传值;
- 将寄存器拆分为高位和低位传递一直比较小的参数;
- 利用堆栈传递参数。
用文字简要描述向Linux 0.11添加一个系统调用foo()的步骤。
我们假设foo()的返回值类型为void。
- 在include/unistd.h中定义宏__NR_foo,并添加供用户调用的函数声明void foo();
- 修改kernel/system_call.s中的nr_system_calls,使其增加1;
- 在include/linux/sys.h中添加函数声明extern void sys_foo(),在系统调用入口表fn_ptr末端添加元素sys_foo;
- 添加kernel/foo.c文件,实现sys_foo()函数;
- 修改kernel/Makefile,在OBJS中加入foo.o,并添加生成foo.s、foo.o的依赖规则。
转载于:https://www.cnblogs.com/tradoff/p/5734582.html
操作系统实验报告-系统调用相关推荐
- 实验报告Linux操作系统基本命令,linux操作系统实验报告全部.doc
linux操作系统实验报告全部 计算机操作系统 实验报告 学 号:姓 名:提交日期:2014.12.15成 绩: 东北大学秦皇岛分校 [实验题目]熟悉Linux/UNIX操作系统[实验目的]1.熟悉L ...
- Linux进程的创建和父子进程同步,操作系统实验报告_Linux进程创建与通信.doc
操作系统实验报告_Linux进程创建与通信 2011-2012学年第一学期 专 业: 班 级: 学 号: 姓 名:提交日期:2011年11月实验二 Linux进程创建与进程通信 [实验目的 1. 熟悉 ...
- 操作系统实验报告18:硬盘柱面访问调度算法
操作系统实验报告18 实验内容 实验内容:硬盘调度. 编写 C 程序模拟实现课件 Lecture25 中的硬盘柱面访问调度算法 包括 FCFS.SSTF.SCAN.C-SCAN.LOOK.C-LOOK ...
- 操作系统实验报告12:线程2
操作系统实验报告12 实验内容 实验内容:线程(2). 编译运行课件 Lecture14 例程代码: Algorithms 14-1 ~ 14-7. 比较 pthread 和 clone() 线程实现 ...
- 操作系统实验报告10:线程1
操作系统实验报告10 实验内容 实验内容:线程(1). 编译运行课件 Lecture13 例程代码: Algorithms 13-1 ~ 13-8 实验环境 架构:Intel x86_64 (虚拟机) ...
- 操作系统实验报告6:进程间通信—共享内存
操作系统实验报告6 实验内容 实验内容:进程间通信-共享内存. (1).验证:编译运行课件 Lecture 08 例程代码: Linux 系统调用示例 reader-writer 问题:Algorit ...
- 操作系统实验报告5:进程的创建和终止
操作系统实验报告5 实验内容 实验内容:进程的创建和终止. 编译运行课件 Lecture 06 例程代码:Algorithm 6-1 ~ 6-6. 实验环境 架构:Intel x86_64 (虚拟机) ...
- 操作系统实验报告4:Linux 下 x86 汇编语言3
操作系统实验报告4 实验内容 验证实验 Blum's Book: Sample programs in Chapter 08, 10 (Basic Math Functions and Using S ...
- 操作系统实验报告1:ucore Lab 1
操作系统实验报告1 实验内容 阅读 uCore 实验项目开始文档 (uCore Lab 0),准备实验平台,熟悉实验工具. uCore Lab 1:系统软件启动过程 (1) 编译运行 uCore La ...
最新文章
- 高效sql性能优化极简教程
- python画椭圆-python opencv圆、椭圆与任意多边形的绘制实例详解
- 图像理解之物体检测object detection,模型rcnn/fastrcnn/fasterrcnn原理及概念
- Elasticsearch SQL介绍及实例
- 解决同一页面中两个iframe互相调用jquery,js函数
- org.apache.hadoop.hdfs.server.namenode.SafeModeException
- C# ASP 面试题 2017
- MyBatis:学习笔记(4)——动态SQL
- java 确定对象的引用_JVM学习笔记之了解对象存活判断和4种引用【三】
- JavaScript冒泡排序算法(1)
- the JAR file spring-beans-4.0.0.RELEASE.jar has no source attachment
- const定义常量_JS声明变量var、let 、const(含重点示例)
- python深度学习——手写字符识别
- 在matlab中,简单地利用RS485协议对伺服电机进行控制
- DWM1000DISCOVERY开发板简介
- Oracle v$SQLAREA
- 如何将wav文件切成多个子文件
- 产品日记(二)一些愚见, 记录在此
- 领航跟随型编队(十四)室内定位技术概述
- “挂羊头卖狗肉”的宇宙学谭
热门文章
- 再见,前端!别更新了,我是学不动了
- micropython 人脸识别检测_Flask实战!从后台管理到人脸识别,六款优质Flask开源项目介绍...
- java ecc signature_如何用python验证android/java的ECC签名
- java中static和this_理解Java中this和static的含义
- redis循环键_Redis 性能优化的 13 条军规!史上最全
- Java运算符及录入
- NYOJ-心急的C小加(贪心)
- redis相关技能积累
- webrequest HttpWebRequest webclient/HttpClient
- 利用环境变量LD_PRELOAD来绕过php disable_function执行系统命令