结果
如何添加一个系统调用
- 在user/user.h中添加系统调用原型
int trace(int);
int sysinfo(struct sysinfo *);
- 在user/usys.pl中添加系统调用存根
entry("trace");
entry("sysinfo");
- 在kernel/syscall.h中添加define
#define SYS_trace 22
#define SYS_sysinfo 23
- 在kernel/syscall.c中添加函数声明等
extern uint64 sys_trace(void);
extern uint64 sys_sysinfo(void);
[SYS_trace] sys_trace,
[SYS_sysinfo] sys_sysinfo,
"trace",
"sysinfo"
- 在kernel中找个文件实现该系统调用函数,比如kernel/sysproc.c文件
解析
1. trace
a. 在kernel/proc.h中的proc中添加一项traceMark,作用见注释:
// 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)
// 掩码是由(1 << 系统调用号)构成的,
// 系统调用号最低为1,所以掩码至少为
// 二进制10,故掩码最低位未利用,可以用该位
// 来标识被跟踪的进程
uint64 traceMask; // 用于trace系统调用跟踪系统调用的掩码
};
b. 修改kernel/syscall.c中的syscall函数以识别掩码并按要求输出
void
syscall(void)
{
int num;
struct proc *p = myproc();
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num]();
// 通过掩码最后一位判断是否跟踪
// 通过掩码其余位判断跟踪对象
if((p->traceMask & 1) == 1 && ((1 << num) & p->traceMask) == (1 << num)) {
printf("%d: syscall %s -> %d\n", p->pid, sysCallStr[num], p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
c. 在kernel/sysproc.c中的sys_fork中的fork(kernel/proc.c)中添加一行:
np->traceMask = p->traceMask; // 跟踪掩码对子进程也要生效
2. sysinfo
a. kernel/syspro.c中添加一个系统调用sys_sysinfo
uint64 sys_sysinfo(void) {
uint64 siI;
struct sysinfo si;
struct proc *p = myproc();
if(argaddr(0, &siI) < 0) {
return -1;
}
si.freemem = getFreeMem();
si.nproc = getNProc();
if(copyout(p->pagetable, siI, (char *)&si, sizeof(si)) < 0) {
return -1;
}
return 0;
}
b. 在kernel/defs.h中添加getFreeMem()和getNProc()的声明
c. 在kernel/kalloc.c中添加getFreeMem函数
/**
* @description: 获取空闲内存的字节数
* @return 空闲内存字节数量
*/
uint64 getFreeMem(void) {
struct run *r;
int count = 0;
acquire(&kmem.lock);
r = kmem.freelist;
while(r) {
++count;
r = r -> next;
}
release(&kmem.lock);
return count * PGSIZE;
}
d. 在kernel/proc.c中添加getNProc函数
/**
* @description: 获取不处于UNUSED状态的进程数
* @return 不处于UNUSED状态的进程数
*/
uint64 getNProc(void) {
uint64 count = 0;
struct proc *p;
for(p = proc; p < &proc[NPROC]; p++) {
if(p -> state != UNUSED) {
++count;
}
}
return count;
}
gdb调试
1. 如何打开gdb服务
a. 一个终端执行
make CPUS=1 qemu-gdb
b. 另一个终端执行
riscv64-unknown-elf-gdb kernel/kernel
或在makefile文件中加上
gdb:
riscv64-unknown-elf-gdb kernel/kernel
这样执行make gdb就能有一样的效果
2.如何连接gdb服务端
一种方式是在.gdbinit文件中加上target remote 127.0.0.1:26000
,另一种方法是启动gdb客户端后执行target remote localhost:26000
bug
1. %u: syscall panic: acquire
xv6未实现%u和%llu等控制符,改用%d就行