实验的代码放在了Github上。

第二个实验是Lab: system calls

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

XV6系统调用

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

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

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

fork:
li a7, SYS_fork
ecall
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匹配时就打印相关信息。

// proc.h
struct proc {
...
// this is for sys_trace()
uint trace_mask;
}; // proc.c
int
fork(void) fork(void)
{
...
// copy trace mask
np->trace_mask = p->trace_mask;
...
} // sysproc.c
uint64
sys_trace(void)
{
uint mask;
if(argint(0, (int*)&mask) < 0)
return -1;
struct proc *p = myproc();
p->trace_mask |= mask;
return 0;
} // syscall.c
void
syscall(void)
{
int num;
struct proc *p = myproc(); num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
uint64 ret = syscalls[num]();
p->trapframe->a0 = ret;
if((1 << num) & p->trace_mask) {
printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], ret);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}

Sysinfo (moderate)

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

// sysproc.c
uint64
sys_sysinfo(void)
{
uint64 info; // user pointer
struct sysinfo kinfo;
struct proc *p = myproc();
if(argaddr(0, &info) < 0){
return -1;
}
kinfo.freemem = freemem();
kinfo.nproc = nproc();
if(copyout(p->pagetable, info, (char*)&kinfo, sizeof(kinfo)) < 0){
return -1;
}
return 0;
}

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

// kalloc.c
uint64
freemem(void)
{
uint64 counter = 0;
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
while(r){
r = r->next;
++counter;
}
release(&kmem.lock);
return counter * PGSIZE;
}

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

// proc.c
uint64
nproc(void)
{
uint64 counter = 0;
struct proc *p; for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state != UNUSED) {
++counter;
}
release(&p->lock);
}
return counter;
}

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. 赶紧收藏!Spring MVC 万字长文笔记,我愿奉你为王者笔记!

    Spring MVC Spring MVC是目前主流的实现MVC设计模式的企业级开发框架,Spring框架的一个子模块,无需整合Spring,开发起来更加便捷. 什么是MVC设计模式? 将应用程序分为 ...

  2. 歌曲网站,教你爬取 mp3 和 lyric

    从歌曲网站,获取音频和歌词的流程: 1, 输入歌曲名,查找网站中存在的歌曲 id 2, 拿歌曲 id 下载歌词 lyric 简单的 url 拼接 3, 拿歌曲 id 下载音频 mp3 先用一个 POS ...

  3. python简单爬去前程无忧信息招聘

    import sys reload(sys) sys.setdefaultencoding('utf-8') import requests import csv from BeautifulSoup ...

  4. winform 跨线程 调用控件

    public delegate void rtbCallBack(string txt); public void rtbAddText(string txt) { if (this.rtb.Invo ...

  5. BPF的可移植性和CO-RE (Compile Once – Run Everywhere)

    BPF的可移植性和CO-RE (Compile Once – Run Everywhere) 在上一篇文章中介绍了提高socket性能的几个socket选项,其中给出了几个源于内核源码树中的例子,如果 ...

  6. layer做阻塞式弹出层的方法

    今天遇到一个问题: layer弹出一个confirm提示窗,然后confirm还没有点击对应的按钮的时候,就已经执行了后续代码,我这里做出的判断是,是否需要进行后续操作,但是因为layer.confi ...

  7. java中装箱,拆箱

    (1)包装类 java中不能定义基本数据类型的对象,但是提供了基本数据类型的包装类来解决这一问题,例如int类型的包装类Integer, 包装类的常用方法有Integer.ParseInt()方法类将 ...

  8. WixVersionControl Wix项目版本控制

    原文链接:https://www.swack.cn/wiki/001565675133949eff0d3d5a51f48288cf6d8248905e28f000/001569821278313e6b ...

  9. new ArrayList(0) 和 new ArrayList() 和一样吗?

    第一感觉是一样的,盲猜后者调用了前者,并传入参数 0.然而,无论是 JDK 7 还是 JDK 8,这两个方法构造的结果都是不一样的.JDK 开发人员在这方面作了优化. JDK 7 在 Java 7 中 ...

  10. 一网打尽,一文讲通虚拟机VirtualBox及Linux使用

    本文将从虚拟机的选择.安装.Linux系统安装.SSH客户端工具使用四个方面来详细介绍Linux系统在虚拟机下的安装及使用方法,为你在虚拟机下正常使用Linux保驾护航. 1.虚拟机的选择 在讲虚拟机 ...