文章目录
System call trace
In this assignment you will add a system call tracing feature that may help you when debugging later labs. You’ll create a new trace system call that will control tracing. It should take one argument, an integer “mask”, whose bits specify which system calls to trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork), where SYS_fork is a syscall number from kernel/syscall.h. You have to modify the xv6 kernel to print out a line when each system call is about to return, if the system call’s number is set in the mask. The line should contain the process id, the name of the system call and the return value; you don’t need to print the system call arguments. The trace system call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.
整体流程图
本次实验要添加一个系统调用trace
这个系统调用可以为每一个进程
添加一个位mask
,用来指定要为哪些系统调用输出调试信息
如何新添加一个系统调用
其实这个流程已经在上面的图讲的很清除了,这里我用文字描述一下
第一步
首先,我们要在user/user.h
上添加trace函数的声明,使得用户态程序可以找到跳板入口函数
第二步
在user/usys.pl
脚本中加入entry("trace")
函数,这是从用户态到内核态的跳板函数
这个脚本会生成risc-v汇编代码user/usys.S
在user/usys.S
中
trace: # 定义用户态跳板函数
li a7, SYS_trace # 将系统调用 id 存入 a7 寄存器
ecall # ecall,调用 system call ,跳到内核态的统一系统调用处理函数 syscall() (syscall.c)
ret
第三步
接着,我们要在kernel/syscall.h
中添加sys_trace
的序号
然后,我们要在kernel/syscall.c
中外部全局声明新的内核调用函数sys_trace
然后,我们要在kernel/syscall.c
的映射表syscalls
中添加一个编号
到系统调用指针
ok, 以上都讲完了,我们来看看系统调用的整个流程
user/user.h:
用户态程序调用跳板函数 trace()user/usys.S
跳板函数 trace() 使用 CPU 提供的 ecall 指令,调用到内核态kernel/syscall.c
到达内核态统一系统调用处理函数 syscall(),所有系统调用都会跳到这里来处理。kernel/syscall.c
syscall() 根据跳板传进来的系统调用编号,查询 syscalls[] 表,找到对应的内核函数并调用。kernel/sysproc.c
到达 sys_trace() 函数,执行具体内核操作
具体代码的实现
在`proc结构里,添加一个mask,这个mask会记录要去trace的system call
//kernel/proc.h
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
struct proc *parent; // Parent process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID
// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
int mask; //存储跟踪号
//fork的时候会把结构体里新增的变量也给复制进去,达到了传参的作用
//新的变量不需要加锁,因为只会被自己所在的进程使用
};
实现sys_trace 函数
//kernel/sysproc.c
uint64 sys_trace(void){
int n;
//argint()用于读取在a0-a5寄存器中传递的系统调用参数
if(argint(0,&n)<0){
return -1;
}
//myproc()函数获取当前进程的struct proc,即PCB
myproc()->mask=n;
return 0;
}
添加 np->mask=p->mask;
//kernel/proc.c
// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
{
int i,<