- System call tracing
实现当调用了trace mask所对应的系统调用时,打印调用该系统调用的进程PID、系统调用的名称、系统调用的返回值。(mask的值为 1 << 系统调用号的值)
1)user/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(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); // here
2)在usys.pl中,加入用户态到内核态的跳板函数
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"); // here
此脚本在运行后会生成usys.S
.global trace
trace:
li a7, SYS_trace # 将系统调用id存入a7寄存器
ecall # 调用ecall,切换到内核态执行系统调用
ret
3)在kernel/syscall.h增加系统调用号
// 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 // 添加新的系统调用
4)在syscall.c声明新的内核调用函数,加入前面定义的编号到系统调用函数的指针
extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void); // 声明内核调用函数
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
[SYS_wait] sys_wait,
[SYS_pipe] sys_pipe,
[SYS_read] sys_read,
[SYS_kill] sys_kill,
[SYS_exec] sys_exec,
[SYS_fstat] sys_fstat,
[SYS_chdir] sys_chdir,
[SYS_dup] sys_dup,
[SYS_getpid] sys_getpid,
[SYS_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep,
[SYS_uptime] sys_uptime,
[SYS_open] sys_open,
[SYS_write] sys_write,
[SYS_mknod] sys_mknod,
[SYS_unlink] sys_unlink,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_trace] sys_trace, // 系统调用编号与内核函数指针的映射
};
5)在proc.h进程结构体定义中增加mask字段
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)
uint64 mask; // mask
};
6)在kernel/sysproc.c中增加sys_trace系统调用实现
uint64
sys_trace(void)
{
int p;
if (argint(0, &p) < 0) {
return -1;
}
myproc()->mask = p;
return 0;
}
为进程设置mask
7)syscall.c中打印进程PID、系统调用的名称、系统调用的返回值
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_mkdir] "mkdir",
[SYS_close] "close",
[SYS_trace] "trace",
};
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->mask >> num) & 1) {
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;
}
}
8)kernel/proc.c修改fork函数,使得子进程可以继承父进程的mask
np->mask = p->mask;
系统调用流程总结:
1)user/user.c trace用户态命令程序 调用跳板函数user/user.h trace跳板函数
2)跳板函数trace() 使用XV6 CPU提供的ecall指令,进入内核态
3)到达内核态统一处理函数syscall()
4)syscall()内核函数,根据a7寄存器上的值获取系统调用函数编号,然后调用内核函数
5)内核函数的执行返回值存入a0寄存器
测试结果:
trace1.PNG
trace2.PNG
- Sysinfo
1)在内核的头文件中声明计算空闲内存、获取运行的进程数的函数声明
// kalloc.c
void* kalloc(void);
void kfree(void *);
void kinit(void);
uint64 freemem(void); // here
// log.c
void initlog(int, struct superblock*);
void log_write(struct buf*);
void begin_op(void);
void end_op(void);
// pipe.c
int pipealloc(struct file**, struct file**);
void pipeclose(struct pipe*, int);
int piperead(struct pipe*, uint64, int);
int pipewrite(struct pipe*, uint64, int);
// printf.c
void printf(char*, ...);
void panic(char*) __attribute__((noreturn));
void printfinit(void);
// proc.c
int cpuid(void);
void exit(int);
int fork(void);
int growproc(int);
pagetable_t proc_pagetable(struct proc *);
void proc_freepagetable(pagetable_t, uint64);
int kill(int);
struct cpu* mycpu(void);
struct cpu* getmycpu(void);
struct proc* myproc();
void procinit(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void setproc(struct proc*);
void sleep(void*, struct spinlock*);
void userinit(void);
int wait(uint64);
void wakeup(void*);
void yield(void);
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);
uint64 nprocess(void); // here
2)kalloc.c中添加计算空闲内存的函数
uint64
freemem(void)
{
acquire(&kmem.lock);
uint64 freemembytes = 0;
struct run *r = kmem.freelist;
while (r) {
freemembytes += PGSIZE;
r = r->next;
}
release(&kmem.lock);
return freemembytes;
}
3)在proc.c中实现计算运行的进程树的函数
uint64
nprocess(void)
{
uint64 cnt = 0;
struct proc *p;
for (p = proc; p < &proc[NPROC]; p++) {
if (p->state != UNUSED) {
cnt++;
}
}
return cnt;
}
4)sysproc.c中添加系统调用,其他设置与sys_trace类似
uint64
sys_sysinfo(void)
{
uint64 addr;
if (argaddr(0, &addr) < 0) {
return -1;
}
struct sysinfo sinfo;
sinfo.freemem = freemem();
sinfo.nproc = nprocess();
struct proc *p = myproc();
if (copyout(p->pagetable, addr, (char *)&sinfo, sizeof(sinfo)) < 0) {
return -1;
}
return 0;
}
5)在user.h添加用户态入口
struct sysinfo;
int sysinfo(struct sysinfo *);
测试结果
sysinfo1.PNG
sysinfo2.PNG