2020 6.s081——Lab2:system calls

左岸的一座白色环形阶梯

浪人正在用和弦练习忧郁

晨曦下的少女听着吉他旋律

在许愿池边巴洛克式的叹息

——许愿池的希腊少女

完整代码见:SnowLegend-star/6.s081 at syscall (github.com)

System call tracing (moderate)

这个实验要求我们跟踪系统调用。

感觉实验说明对mask的解释有点语焉不详,研究了好一番才明白mask的作用:把mask转换为32-bit的二进制数n,第ni 位为1就跟踪编号为i的syscall。而且说明里面应该还有个错误,int类型的最大正数应为“2147483647”,而实验说明里搞了个这数,我也是看半天没反应过来,可恶。

       读懂了实验要求,就可以着手实验部分了。根据hints,我们可以大致设计如下实现流程:

       ①确定syscall函数名sys_trace

       ②在syscall.h中给sys_trace设定一个系统调用编号

       ③在syscall.c中注册sys_trace,同时建立一个指针数组*syscallsName[]来记录系统调用的函数名称。

       ④在user.h中定义函数trace(int),在usys.pl中设置sys_trace的调用入口。

       ⑤完成sys_trace在各个头文件的声明之后,在struct proc中添加一个成员mask,从而能够使父进程将mask传递给子进程。

       确定好大致流程后,进行sys_trace()的实现工作。这里说一点,貌似系统调用函数都是不用传参的,大概是为了保证操作系统的isolation。用户空间与内核空间的传参会有专门的实验函数维护。那么问题来了,我们得想办法从用户空间吧mask拿过来。我们注意到hints里有这句话

The functions to retrieve system call arguments from user space are in kernel/syscall.c, and you can see examples of their use in kernel/sysproc.c

结合xv6文档的4.4节

The functions argint, argaddr, and argfd retrieve the n ’th system call argument from the trap frame as an integer, pointer, or a file descriptor.

看完上面两点之后就大致有点头绪了,然后我们在sysproc.c文件里面浏览一番,看看有没有哪些函数调用了argint()这种函数。可以发现调用形式都是argint(0,&argument)。这就好办了,我们来个argint(0,&mask)应该就可以从用户空间里获得mask。Ok,现在有了mask这个参数,剩下的就是把它传递给当前进程了。

还是观察sysproc.c内部的那些函数,用相对敏锐的注意力察觉到myproc()可能有点作用。浏览一番定义果真是获取当前进程的函数。那么sys_trace到这里也就结束了。最后在syscall.c函数里面修改打印内容即可。当然,这里得用上a0和a7这两个寄存器,xv6文档内部也有提到,就略过不表了。

此时再顺便把进程相关的结构体浏览一番(proc.h),对进程的处理有个大致的了解后为第二个实验打下基础。

sys_trace()如下
 

//跟踪系统调用情况
uint64
sys_trace(void){
  int mask;
  if(argint(0,&mask)<0)
    return -1;
  myproc()->mask=mask;
  return 0;
}

Sysinfo (moderate)

完成这个实验之前也可以自己设计个大致流程,和sys_trace比较相似,就不再赘述了。我们观察struct sysinfo这个结构体,可以确定主要任务就是获取它其中的两个成员: freemen和nproc。这里定义两个函数kfmstat()和sum_USED来分别统计系统剩余内存和process->state不为UNUSED

kfmstat()根据hints可以在kalloc.c中实现。在完成它之前,我们还是老样子观察下kalloc.c里的其他函数是怎么实现的。可以发现系统用freelist来维护内存分配。那kfmstat()的实现核心也就呼之欲出了——遍历一遍freelist。

Tips: freelist中,每个内存节点的大小都是4096B

收集进程数则是在proc.c中实现,这里还有个现成的函数procdump()统计系统状态。其实偷懒点直接改造procdump()都行。由于太过简单就略过不表。

这个实验最难的地方是在sys_sysinfo自己的函数体。在sys_sysinfo()内部调用完上述两个函数初始化sysINFO后,我们要怎么把这个结构体给传递到用户空间呢?

我们按照hints查看sys_fstat()  (kernel/sysfile.c)和filestat() ( kernel/file.c ),可以发现一条及其重要的注释

先通过argaddr()函数得到用户空间

sysinfo(info)

传过来的这个地址,存放在uint64类型的变量中。(注意,用户空间传过来的地址一定要放在uint64类型的变量中,不可以放在struct sysinfo类型中)然后再使用copyout函数把内容从内核空间拷贝进用户空间中。

我们进入vm.c中查看下copyout()的实现方法

// Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error.
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)

接下来就可以顺便将内核空间中的sysINFO传递出去了。

在完成这个lab的时候,我突然想到一个问题,用户空间中使用的函数是trace(int n),但是内核空间使用的却是sys_trace(void),那用户空间传递的这个n到底是怎么被内核接收到的呢?

其实这个问题的答案在上面已经提到过了,sys_trace(void)不能直接通过传参的方式接收来自用户空间的参数,是通过argint()等函数来接收来自用户空间的参数

最后,我有一个问题始终没有得到解决。当我使用argaddr来接收参数时

argaddr(1,&sysINFO_user)

如果把第一个参数设置为1则会报错

我的猜测由于argaddr第一个参数其实是和寄存器相关的,传参时系统会默认按序使用寄存器。那么来自用户空间的sysinfo地址就被存在第一个寄存器a0里面了,而不是存在第二个寄存器a1内部。                                          

见Xv6-2020文档的4.4节

The functions argint, argaddr, and argfd retrieve the n ’th system call argument from the trap frame as an integer, pointer, or a file descriptor

sys_info()如下

uint64
sys_sysinfo(void){
  struct sysinfo sysINFO;
  struct proc *p=myproc();
  uint64 sysINFO_user;    //user pointer to struct sysinfo     
  sysINFO.freemem=kfmstat();  //刚才用sys_checkmem是有问题的
  sysINFO.nproc=sum_UNUSED();

  // printf("In kernel, available memory: %dB\n", sysINFO.freemem);
  // printf("In kernel, UNUSED process: %d\n", sysINFO.nproc);

  if(argaddr(0,&sysINFO_user)<0)
    return -1;
  // sysINFO_user=(struct sysinfo)sysINFO_user;
  // printf("The sysinfo from user space: \n")
  // printf("available memory: %d",sysINFO_user);
  if(copyout(p->pagetable, sysINFO_user, (char *)&sysINFO, sizeof(sysINFO)) < 0)
    return -1;

  return 0;
}

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值