实验的代码放在了Github上。

第二个实验是Lab: system calls

这个实验主要就是自己实现几个简单的系统调用并添加到XV6中。

XV6系统调用

添加系统调用主要有以下几步:

user/user.h中添加系统调用函数的定义。

user/usys.pl中添加入口,这个文件将会在make后生成user/usys.S文件,在该汇编文件中,每个函数就只有三行,将系统调用号通过li(load imm)存入a7寄存器,之后使用ecall进入内核态,最后返回。

  1. fork:
  2. li a7, SYS_fork
  3. ecall
  4. ret

kernel/syscall.h中定义系统调用号。

kernel/syscall.csyscalls函数指针数组中添加对应的函数。在syscall函数中,先读取trapframe->a7获取系统调用号,之后根据该系统调用号查找syscalls数组中的对应的处理函数并调用。

System call tracing (moderate)

先在proc结构体中添加一个trace_mask字段,之后在fork函数中复制该字段到新进程。

在系统调用sys_trace中就只要通过argint函数读取参数,然后设置给trace_mask字段就行了。

最后修改syscall,当系统调用号和trace_mask匹配时就打印相关信息。

  1. // proc.h
  2. struct proc {
  3. ...
  4. // this is for sys_trace()
  5. uint trace_mask;
  6. };
  7. // proc.c
  8. int
  9. fork(void) fork(void)
  10. {
  11. ...
  12. // copy trace mask
  13. np->trace_mask = p->trace_mask;
  14. ...
  15. }
  16. // sysproc.c
  17. uint64
  18. sys_trace(void)
  19. {
  20. uint mask;
  21. if(argint(0, (int*)&mask) < 0)
  22. return -1;
  23. struct proc *p = myproc();
  24. p->trace_mask |= mask;
  25. return 0;
  26. }
  27. // syscall.c
  28. void
  29. syscall(void)
  30. {
  31. int num;
  32. struct proc *p = myproc();
  33. num = p->trapframe->a7;
  34. if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
  35. uint64 ret = syscalls[num]();
  36. p->trapframe->a0 = ret;
  37. if((1 << num) & p->trace_mask) {
  38. printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], ret);
  39. }
  40. } else {
  41. printf("%d %s: unknown sys call %d\n",
  42. p->pid, p->name, num);
  43. p->trapframe->a0 = -1;
  44. }
  45. }

Sysinfo (moderate)

这一个系统调用主要就是要实现freememnproc两个函数来统计内存和进程。

  1. // sysproc.c
  2. uint64
  3. sys_sysinfo(void)
  4. {
  5. uint64 info; // user pointer
  6. struct sysinfo kinfo;
  7. struct proc *p = myproc();
  8. if(argaddr(0, &info) < 0){
  9. return -1;
  10. }
  11. kinfo.freemem = freemem();
  12. kinfo.nproc = nproc();
  13. if(copyout(p->pagetable, info, (char*)&kinfo, sizeof(kinfo)) < 0){
  14. return -1;
  15. }
  16. return 0;
  17. }

阅读kallockfree两个函数就可以知道,kmem.freelist是一个保存了当前空闲内存块的链表,因此只需要统计这个链表的长度再乘以PGSIZE就可以得到空闲内存。

  1. // kalloc.c
  2. uint64
  3. freemem(void)
  4. {
  5. uint64 counter = 0;
  6. struct run *r;
  7. acquire(&kmem.lock);
  8. r = kmem.freelist;
  9. while(r){
  10. r = r->next;
  11. ++counter;
  12. }
  13. release(&kmem.lock);
  14. return counter * PGSIZE;
  15. }

阅读procdump和相关代码就可以知道,XV6的进程结构体保存在proc[NPROC]数组当中。而proc->state字段保存了PCB的当前状态,有UNUSED、SLEEPING、RUNNABLE、RUNNING、ZOMBIE五种状态。因此只需要遍历这个数组,然后统计state不是UNUSED状态的就行了。

  1. // proc.c
  2. uint64
  3. nproc(void)
  4. {
  5. uint64 counter = 0;
  6. struct proc *p;
  7. for(p = proc; p < &proc[NPROC]; p++) {
  8. acquire(&p->lock);
  9. if(p->state != UNUSED) {
  10. ++counter;
  11. }
  12. release(&p->lock);
  13. }
  14. return counter;
  15. }

XV6学习(2)Lab syscall的更多相关文章

  1. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

  2. xv6学习笔记(3):中断处理和系统调用

    xv6学习笔记(3):中断处理和系统调用 1. tvinit函数 这个函数位于main函数内 表明了就是设置idt表 void tvinit(void) { int i; for(i = 0; i & ...

  3. xv6学习笔记(4) : 进程调度

    xv6学习笔记(4) : 进程 xv6所有程序都是单进程.单线程程序.要明白这个概念才好继续往下看 1. XV6中进程相关的数据结构 在XV6中,与进程有关的数据结构如下 // Per-process ...

  4. xv6学习笔记(5) : 锁与管道与多cpu

    xv6学习笔记(5) : 锁与管道与多cpu 1. xv6锁结构 1. xv6操作系统要求在内核临界区操作时中断必须关闭. 如果此时中断开启,那么可能会出现以下死锁情况: 进程A在内核态运行并拿下了p ...

  5. XV6学习笔记(1) : 启动与加载

    XV6学习笔记(1) 1. 启动与加载 首先我们先来分析pc的启动.其实这个都是老生常谈了,但是还是很重要的(也不知道面试官考不考这玩意), 1. 启动的第一件事-bios 首先启动的第一件事就是运行 ...

  6. XV6学习(1) Lab util

    正在学习MIT的6.S081,把做的实验写一写吧. 实验的代码放在了Github上. 第一个实验是Lab util,算是一个热身的实验,没有涉及到系统的底层,就是使用系统调用来完成几个用户模式的小程序 ...

  7. XV6学习(16)Lab net: Network stack

    最后一个实验了,代码在Github上. 这一个实验其实挺简单的,就是要实现网卡的e1000_transmit和e1000_recv函数.不过看以前的实验好像还要实现上层socket相关的代码,今年就只 ...

  8. XV6学习(9)Lab cow: Copy-on-write fork

    代码在github上.总体来说如果理解了COW机制的话,这个实验的完成也没有很复杂. 这一个实验是要完成COW(copy on write)fork.在原始的XV6中,fork函数是通过直接对进程的地 ...

  9. XV6学习(11)Lab thread: Multithreading

    代码放在github上. 这一次实验感觉挺简单的,特别是后面两个小实验.主要就是对多线程和锁进行一个学习. Uthread: switching between threads 这一个实验是要实现一个 ...

随机推荐

  1. Unity 打包Shader优化

    我们一直以来的项目Shader基本都会打包到一个package里面,游戏启动时会进行预加载这个Package,且预加载其中一些常用的Shader,最近新发现一个坑点,那就是shader依赖了特效的一些 ...

  2. 5. 穿过拥挤的人潮,Spring已为你制作好高级赛道

    目录 ✍前言 版本约定 ✍正文 默认转换器注册情况 StreamConverter 代码示例 使用场景 兜底转换器 ObjectToObjectConverter part1:快速返回流程 part2 ...

  3. Nginx-rtmp+ FFmpeg +Docker + vue.js 直播系统搭建

    思路(如图): 1,开启推流服务器(这里我的Nginx-rtmp服务器搭建成功) 进入docker 开启推流服务器  docker run -it -p 1935:1935 -p 8000:80 -- ...

  4. K-NN(最近邻分类算法 python

    # algorithm:K-NN(最近邻分类算法)# author:Kermit.L# time: 2016-8-7 #======================================== ...

  5. C# 委托、事件、表达式树理解

    1.什么是委托? 委托是一种动态调用方法的类型,属于引用型. 委托是对方法的抽象和封装.委托对象实质上代表了方法的引用(即内存地址) 所有的异步都是委托   委托就是函数当入参   委托被各种语法糖遮 ...

  6. Omega System Trading and Development Club内部分享策略Easylanguage源码

    更多精彩内容,欢迎关注公众号:数量技术宅.关于本期分享的任何问题,请加技术宅微信:sljsz01 关于 Omega System Trading and Development Club " ...

  7. OpenGL投影矩阵(Projection Matrix)构造方法

    (翻译,图片也来自原文) 一.概述 绝大部分计算机的显示器是二维的(a 2D surface).在OpenGL中一个3D场景需要被投影到屏幕上成为一个2D图像(image).这称为投影变换(参见这或这 ...

  8. SAML和OAuth2这两种SSO协议的区别

    目录 简介 SAML SAML的缺点 OAuth2 OAuth2的缺点 两者的对比 CAS简介 简介 SSO是单点登录的简称,常用的SSO的协议有两种,分别是SAML和OAuth2.本文将会介绍两种协 ...

  9. TurtleBot3使用课程-第二节a(北京智能佳)

    目录 1.[第3类]LRF(LDS)传感器 2 1.1 传感器包安装 2 1.1.1 传感器端口访问设置 2 1.1.2 运行hlds_laser_publisher节点 2 1.1.3 在RViz中 ...

  10. Failed to create Spark client for Spark session

    最近在hive里将mr换成spark引擎后,执行插入和一些复杂的hql会触发下面的异常: org.apache.hive.service.cli.HiveSQLException: Error whi ...