6.S081 lab2 记录及实现

一些杂七杂八的话

本次的lab实验从lab2开始记录,lab1全部属于用户程序,难度较低,写lab2的时候我感受到了困难,决定记录一下,以后大概一周更新一个lab吧。

环境

我的linux是win上的ubuntu20.04子系统,配合vscode可以很方便的开发。vscode c++插件的函数追踪功能可以方便的让我们看到函数之间是怎么跳转的。

发生了什么

找到用户文件夹任意一个已有的系统调用,例如fork(),如果你转到定义,你会发现这些系统调用在user文件夹内只有声明,没有实现。而在kernel文件夹内的系统调用名(sysproc)又全部是uint64 sys_fork(viod)这种形式,感觉无法连接到一起。实际上,在编译时会调用user/usys.pl脚本来生产系统调用接口的汇编代码
由usys.pl产生系统调用接口
li a7, SYS_xxx把系统调用编号移入寄存器a7
然后调用ecall进入kernel/syscall.c的syscall函数,再利用a7的值找到对应的系统调用。

那么我们要更改的文件一目了然,

  1. makefile里添加用户程序
  2. ./user/usys.pl后添加entry("trace"); entry("sysinfo");
  3. ./kernel/syscall.h 下添加两个系统调用编号#define SYS_trace 22
  4. ./kernel/syscall.c 的系统调用函数数组里添加这两个调用,同时声明extern uint64 sys_trace(void)
  5. ./user/user.h 下添加函数原型int trace(int)
  6. 在./kernel/sysproc.c下添加对应系统调用即可

trace

trace要求按照mask的表示追踪使用的系统调用。mask是个64位的uint,系统调用总计20多个,所以可以利用第i位标识第i个系统调用是否需要追踪。那么(1<<SYS_call)即可得到第i位是1的值,再和mask按位与,大于0就是需要跟踪的系统调用。即判断条件是if((1<<num) & mask)。

所以我们需要设计让程序能看见此时的mask值,自然想到直接把mask变量加入到结构体proc(kernel/proc.h内)
在这里插入图片描述
trace系统调用的功能即给proc.mask赋值,在这里插入图片描述
然后还需要调整fork()的代码,保证mask会被子进程继承,赋值即可
在这里插入图片描述
最后,修改syscall.c的syscall函数,让它按要求输出
在这里插入图片描述
其中的syscallname是在前面定义的系统调用名称数组。
测试如下:在这里插入图片描述

sysinfo

sysinfo要求统计系统内的剩余内存以及进程数,那么从系统初始化开始记录每次的操作即可,释放内存就info.freemem += PGSIZE,分配内存就info.freemem -= PGSIZE; fork()如果是子进程,就++info.nproc,父进程不操作,完成exit()前–info.nproc即可。
注意在user/user.h声明函数原型时要先声明struct info。

int sysinfo(struct sysinfo *info)本身非常简单,只要把系统内部的kinfo(我定义的内核中的全局struct sysinfo变量)拷贝到传入的info即可,所以sysproc.c 下的sysinfo如下

uint64
sys_sysinfo(void)
{
  uint64 addr;
  argaddr(0,&addr);
  if(copyout(myproc()->pagetable,addr,(char *)&kinfo,sizeof(kinfo)) < 0)
    return -1;
  return 0;
}

但是要维护kernel内的kinfo需要调整内核的其他函数。

  1. kernel/kalloc.c
    此文件里都是关于内存分配的,我在这里定义strcut sysinfo kinfo; 并在之后的函数中赋初值
    定位到kinit()函数,此函数是初始化内存用的,可以看到关键是调用了freeange来获取空白内存,其内部关键又是调用kfree§;
void
kinit()
{
    kinfo.freemem = 0;
    kinfo.nproc = 0;
    initlock(&kmem.lock, "kmem");
    freerange(end, (void*)PHYSTOP);
}

void
freerange(void *pa_start, void *pa_end)
{ 
    char *p;
    p = (char*)PGROUNDUP((uint64)pa_start);
    for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE){
  	    //kinfo.freemem+=PGSIZE;
  	    kfree(p);
    }
}

在kinit()内给kinfo赋初值,先都设为0,freerange会不停调用kfree,在kfree()里每释放一次内存,kinfo.freemem加一个PGSIZE;在kalloc()里每成功分配一次内存就kinfo.freemem减一个PGSIZE,很简单的就完成了空余内存的维护。

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  // Fill with junk to catch dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;

  acquire(&kmem.lock);
  r->next = kmem.freelist;
  kmem.freelist = r;
  kinfo.freemem+=PGSIZE;
  release(&kmem.lock);
}

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r){
    kmem.freelist = r->next;
    
  }
  release(&kmem.lock);

  if(r){
    kinfo.freemem-=PGSIZE;
    memset((char*)r, 5, PGSIZE); // fill with junk
  }
    
  
  return (void*)r;
}

然后调整fork()和exit(),在kernel/proc.c里。
对于exit(),在唤醒父进程之前–kfino.nproc;
里面有这么一行注释

// Parent might be sleeping in wait().
  -- kinfo.nproc;
  wakeup1(original_parent)

对于fork(),在返回0之前加++kinfo.nproc;

  // Cause fork to return 0 in the child.
  ++kinfo.nproc;
  np->trapframe->a0 = 0;

这样就完成了,最后提醒记得在sysproc.c proc.c里include sysinfo.h以及声明extern struct sysinfo kinfo;

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值