MIT 6.S081 Lab1系统调用

相关知识

进程最重要的内核状态:1. 页表 p->pagetable 2. 内核堆栈p->kstack 3. 运行状态p->state,显示进程是否已经被分配、准备运行/正在运行/等待IO或退出

每个进程中都有线程(thread),是执行进程命令的最小单元,可以被暂停和继续

每个进程有两个堆栈:用户堆栈(user stack)和内核堆栈(kernel stack)。当进程在user space中进行时只使用用户堆栈,当进程进入了内核(比如进行了system call)使用内核堆栈

在_entry中设置了一个初始stack,stack0来让xv6执行kernel/start.c。在start函数先在machine模式下做一些配置,然后通过RISC-V提供的mret指令切换到supervisor mode,使program counter切换到kernel/main.c

main先对一些设备和子系统进行初始化,然后调用kernel/proc.c中定义的userinit来创建第一个用户进程。这个进程执行了一个initcode.S的汇编程序,这个汇编程序调用了exec这个system call来执行/init,重新进入kernel。exec将当前进程的内存和寄存器替换为一个新的程序(/init),当kernel执行完毕exec指定的程序后,回到/init进程。/init(user/init.c)创建了一个新的console device以文件描述符0,1,2打开,然后在console device中开启了一个shell进程,至此整个系统启动了

trace函数

添加系统调用函数

要求:trace [tracing_mask] [command] 要求当调用了给定的tracing mask所对应的system call时,打印输出调用该system call的进程PID、system call的名称、系统调用号。已经给出了user space下的user/trace.c,需要注册并实现trace这一system call

user/user.h:		用户态程序调用跳板函数 trace()
user/usys.S:		由user/usys.pl编译而来。跳板函数 trace() 使用 CPU 提供的 ecall 指令,调用到内核态
kernel/syscall.c	到达内核态统一系统调用处理函数 syscall(),所有系统调用都会跳到这里来处理。
kernel/syscall.c	syscall() 根据跳板传进来的系统调用编号,查询 syscalls[] 表,找到对应的内核函数并调用。
kernel/sysproc.c	到达 sys_trace() 函数,执行具体内核操作

第一步:用户空间配置

user.h中声明函数。
用户态文件夹下实现用户态的trace函数入口
user.pl中ecall指令,进入syscall

第二步:内核空间配置

/*,所有的系统调用到达内核态后,都会进入到 syscall() 这个函数进行处理,
所以要跟踪所有的内核函数,只需要在 syscall() 函数里埋点就行了。*/

void
syscall(void)
{
  int num;
  struct proc *p = myproc();  

  num = p->trapframe->a7;
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    // Use num to lookup the system call function for num, call it,
    // and store its return value in p->trapframe->a0
    p->trapframe->a0 = syscalls[num]();  //a0中是系统调用地址
	if((p->syscall_trace >> num) & 1) {
      printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], p->trapframe->a0); // syscall_names[num]: 从 syscall 编号到 syscall 名的映射表
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

该函数首先需要知道是发生了哪个syscall,所以会取a7查看对应的系统调用号。最后,为了能够按照题目要求实现打印输出,需要判断相关逻辑,系统调用名字存放在syscall_names变量中

由于内核与用户进程的页表不同,寄存器也不互通,所以参数无法直接通过 C 语言参数的形式传过来,而是需要使用 argaddr、argint、argstr 等系列函数,从进程的 trapframe 中读取用户进程寄存器中的参数。

同时由于页表不同,指针也不能直接互通访问(也就是内核不能直接对用户态传进来的指针进行解引用),而是需要使用 copyin、copyout (类似linux中copy_from_usr)方法结合进程的页表,才能顺利找到用户态指针(逻辑地址)对应的物理内存地址。(在本 lab 第二个实验会用到)

函数实现逻辑为:首先需要获取系统调用的参数,也就是从用户态传入的参数mask,然后设置当前进程的syscall_trace为该参数(需要在进程结构体中添加该项),同时为了能够保证真正地trace指定的系统调用,那么就不仅仅在该进程,同时也需要相关所有的子进程也传递该参数,就需要在fork.c函数中增加一行赋值语句

Sysinfo

添加一个系统调用,返回空闲的内存、以及已创建的进程数量。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值