system calls
Using gdb
vscode debug:
https://www.cnblogs.com/KatyuMarisaBlog/p/13727565.html
https://pdos.csail.mit.edu/6.828/2022/labs/syscall.html
操作步骤:
新打开一个窗口在项目当前目录下:
make qemu-gdb
最后会得到一个端口号,
再打开一个新的窗口
riscv64-unknown-elf-gdb
target remote localhost:<PORT NUMBER FROM POINT 6 ABOVE>(刚才得到的端口)
// 所有内核的文件表向
file kernel/kernel
//比如你要断点ls的文件
file user/_ls
B 挂载断点
C 下一个断点
N 单步断点
System call tracing :
在本作业中,您将添加一个系统调用跟踪功能,该功能可能会在以后调试实验室时对您有所帮助。您将创建一个新的跟踪系统调用来控制跟踪。它应该有一个参数,一个整数“掩码”,其位指定要跟踪的系统调用。例如,要跟踪fork系统调用,程序调用trace(1<<SYS_fork),其中SYS_fork是kernel/syscall.h中的系统调用编号。如果系统调用的编号在掩码中设置,则必须修改xv6内核,以便在每个系统调用即将返回时打印出一行。该行应包含进程id、系统调用的名称和返回值;您不需要打印系统调用参数。跟踪系统调用应启用对调用它的进程及其随后派生的任何子进程的跟踪,但不应影响其他进程。
CPU model:
CPU can execute instructions: machine mode, supervisor mode, and user mode
主要就是要搞清楚需要在哪些地方添加代码,当你添加代码时:
Userspace
1、 makefile 新增 .c 调用
2、 user.h 声明函数
3、usys.pl 函数调用跳板:Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs。
Kernelspace
syscall.h 声明 kernel函数编号
syscall.c. 添加配置
sysproc.c 添加函数具体实现
±----------------+
| User Process |
±----------------+
|
|
V
±----------------+
| System Call |
| Wrapper |
±----------------+
|
|
V
±----------------+
| usys.S |
| Assembly |
±----------------+
|
|
V
±----------------+
| syscall() |
| in syscall.c |
±----------------+
|
|
V
±----------------+
| Kernel |
| Function |
±----------------+
|
|
V
±----------------+
| System Call |
| Interface |
±----------------+
makefile
UPROGS=\
$U/_cat\
$U/_echo\
$U/_forktest\
$U/_grep\
$U/_init\
$U/_kill\
$U/_ln\
$U/_ls\
$U/_mkdir\
$U/_rm\
$U/_sh\
$U/_stressfs\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_trace\
user.h
新增 trace 函数
// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(const char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int);
user.h
新增 trace 函数,提供给用户调用
// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(const char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int);
user/usys.pl 用户态trap进内核跳板,(makefile 会生成 user/usys.S, RISC-V ecall 进trap)
// system calls
entry("fork");
entry("exit");
entry("wait");
entry("pipe");
entry("read");
entry("write");
entry("close");
entry("kill");
entry("exec");
entry("open");
entry("mknod");
entry("unlink");
entry("fstat");
entry("link");
entry("mkdir");
entry("chdir");
entry("dup");
entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("trace");
内核态
系统调用映射
kernel/syscall.h 新增trace
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_read 5
#define SYS_kill 6
#define SYS_exec 7
#define SYS_fstat 8
#define SYS_chdir 9
#define SYS_dup 10
#define SYS_getpid 11
#define SYS_sbrk 12
#define SYS_sleep 13
#define SYS_uptime 14
#define SYS_open 15
#define SYS_write 16
#define SYS_mknod 17
#define SYS_unlink 18
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_trace 22
系统调用实现
kernel/sysproc.c
uint64
sys_trace(void)
{
int traceid;
argint(0, &traceid);
myproc()->mask=traceid;
return traceid;
}
给进程新增一个mask 用于记录它的traceid
kernel/proc.h
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
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
uint64 mask; // trace Id
// wait_lock must be held when using this:
struct proc *parent; // Parent process
// 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)
};
子进程也需要传递该trace id
// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
np->mask=p->mask;
在系统调用入口打印
kernel/syscall.c
// Prototypes for the functions that handle system calls.
extern uint64 sys_trace(void);
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
static uint64 (*syscalls[])(void) = {
[SYS_trace] sys_trace,
//新增名字映射
const char *syscall_names[] = {
[SYS_fork] "fork",
[SYS_exit] "exit",
[SYS_wait] "wait",
[SYS_pipe] "pipe",
[SYS_read] "read",
[SYS_kill] "kill",
[SYS_exec] "exec",
[SYS_fstat] "fstat",
[SYS_chdir] "chdir",
[SYS_dup] "dup",
[SYS_getpid] "getpid",
[SYS_sbrk] "sbrk",
[SYS_sleep] "sleep",
[SYS_uptime] "uptime",
[SYS_open] "open",
[SYS_write] "write",
[SYS_mknod] "mknod",
[SYS_unlink] "unlink",
[SYS_link] "link",
[SYS_mkdir] "mkdir",
[SYS_close] "close",
[SYS_trace] "trace",
[SYS_sysinfo] "sysinfo",
};
//修改syscall 当trace id 等于 syscall id 的时候就打印
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]();
if( p->mask & (1<<num) ){
printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
Sysinfo
在这个分配中,您将添加一个系统调用sysinfo,用于收集有关正在运行的系统的信息。系统调用采用一个参数:指向结构sysinfo的指针(请参阅kernel/sysinfo.h)。内核应该填写此结构的字段:freemem字段应该设置为可用内存的字节数,nproc字段应该设为状态不是UNUSED的进程数。我们提供一个测试程序sysinfotest;如果它打印出“sysinfotest:OK”,那么您就通过了这个赋值。
系统调用过程: 添加sysinfo相关
Userspace
1、 makefile 新增 .c 调用
2、 user.h 声明函数
3、usys.pl 函数调用跳板:Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs。
Kernelspace
syscall.h 声明 kernel函数编号
syscall.c. 添加配置
sysproc.c 添加函数具体实现
kalloc.c
在手册后续中会告诉你 空闲的空间通过 freelist 管理,这里参考kalloc 函数
uint64 get_last_mem(void){
acquire(&kmem.lock);
uint64 last_mem=0;
struct run *r=kmem.freelist;
while(r){
last_mem+=PGSIZE;
r=r->next;
}
release(&kmem.lock);
printf("last_mem:%d \n",last_mem);
return last_mem;
}
在proc.c中添加,具体参考procdump
uint64
get_active_process_num(void)
{
struct proc *p;
uint64 cnt=0;
for(p = proc; p < &proc[NPROC]; p++) {
if(p->state != UNUSED){
cnt++;
}
}
return cnt;
}
sys_pro.c
新增sys_sysinfo,这里 copyout(p->pagetable, addr, (char *)&info, sizeof(info)) 的使用具体可以参考file.c 和函数定义
uint64
sys_sysinfo(void)
{
uint64 addr; // user pointer to arg addr
argaddr(0, &addr);
struct sysinfo info;
struct proc *p = myproc();
info.freemem=get_last_mem();
info.nproc=get_active_process_num();
if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
return -1;
return 0;
}
总结
对于lab2 需要学习到,一次从用户态到内核态的调用过程是怎么样执行的,怎么用gdb去分析内存代码。