文章目录
EXERCISE 0 源代码阅读
阅读下面两个源代码,理解xv6及其系统调用
syscall.h
是对xv6里常见的21个系统调用的宏定义。定义指向实现函数的系统调用向量的位置。
在syscall.c中外部定义(extern)一个链接内核和shell的函数。
// 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
sysproc.c
与进程创建、退出等相关的系统调用函数的实现。
#include "types.h" // typedef uint, ushort, uchar; uint8, uint16, uint32, uint64; pde_t;
#include "riscv.h"
#include "defs.h" // struct & functions
#include "date.h" // struct rtcdate{uint second, minute, hour, day, month, year; }
#include "param.h" // define NPROC NCPU NOFILE NFILE NINODE NDEV ROOTDEV MAXARG MAXOPBLOCKS LOGSIZE NBUF FSSIZE MAXPATH
#include "memlayout.h" // define UART0 UART0_IRQ VIRTIO0 VIRTIO_IRQ PLIC PLIC_PRIORITY PLIC_PENDING PLIC_MENABLE PLIC_SENABLE PLIC_MPRIORITY PLIC_SPRIORITY PLIC_MCLAIM PLIC_SCLAIM KERNBASE PHYSTOP TRAMPOLINE KSTACK TRAPFRAME
#include "spinlock.h" // struct spinlock{uint locked; char *name; struct cpu *cpu;}
#include "proc.h" // struct context{uint64 ra, sp, s0~s11;} cpu{proc *proc; context *context; int noff, intena;} trapframe{uint64 kernel_satp, kernel_sp, kernel_trap, epc, kernel_hartid, ra, sp, gp, tp, t0~t2, s0~s1, a0~a7, s2~s11, t3~t6;} enum procstate {UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE} proc{spinlock lock; procstate state; void *chan; int killed, xstate, pid; uint64 kstack, sz; page_table_t pagetable; trapframe *trapframe; context context; file *ofile[NOFILE]; inode *cwd; char name[16]}
uint64
sys_exit(void)
{
int n;
if(argint(0, &n) < 0)
return -1;
exit(n);
return 0; // not reached
}
uint64
sys_getpid(void)
{
return myproc()->pid;
}
uint64
sys_fork(void)
{
return fork();
}
uint64
sys_wait(void)
{
uint64 p;
if(argaddr(0, &p) < 0)
return -1;
return wait(p);
}
uint64
sys_sbrk(void)
{
int addr;
int n;
if(argint(0, &n) < 0)
return -1;
addr = myproc()->sz;
if(growproc(n) < 0)
return -1;
return addr;
}
uint64
sys_sleep(void)
{
int n;
uint ticks0;
if(argint(0, &n) < 0)
return -1;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){
if(myproc()->killed){
release(&tickslock);
return -1;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
}
uint64
sys_kill(void)
{
int pid;
if(argint(0, &pid) < 0)
return -1;
return kill(pid);
}
// return how many clock tick interrupts have occurred
// since start.
uint64
sys_uptime(void)
{
uint xticks;
acquire(&tickslock);
xticks = ticks;
release(&tickslock);
return xticks;
}
sysproc.c中的系统调用函数都没有参数,而内部的kill, sleep函数却含有参数。这是因为在XV6中无法将参数从用户级函数传递到内核级函数,例如:XV6中用argint()函数来传递整数。
argint(); // 传递整数
argptr(); //传递指针
argstr(); // 传递字符串起始地址
EXERCISE 1 运行xv6
安装环境:参考官方文档xv6-tools
克隆xv6仓库: git://g.csail.mit.edu/xv6-labs-2020
运行xv6:make qemu
退出qemu:Ctrl-a + x
自我评分:make grade
EXERCISE 2 sleep
XV6 book Chapter1
Operating system interfaces
xv6:一个kernel,许多进程(指令、数据、堆栈)
内容:processes, memory, file descriptors, pipes, a file system.
shell:a user program. xv6 shell的实现在user/sh.c:1中可以找到。
Xv6 system calls
If not otherwise stated, these calls return 0 for no error, and -1 if there’s an error.
int fork();
int exit(int status);
int wait(int *status);
int kill(int pid);
int getpid();
int sleep(int n);
int exec(char *file, char *argv[]);
char *sbrk(int n);
int open(char *file, char *argv[]);
int write(int fd, char *buf, int n);
int read(int fd, char *buf, int n);
int close(int fd);
int dup(int fd);
int pipe(int p[]);
int chdir(char *dir);
int mkdir(char *dir);
int mknod(char *file, int, int);
int fstat(int fd, struct stat *st);
int stat(char *file, struct stat *st);
int link(char *file1, char *file2);
int unlink(char *file);
1.1 Processes and memory
fork创建一个子进程,内容和父进程相同。父级fork返回子级的PID,子级fork返回0.
ELF格式。exec有两个参数:包含可执行参数的文件名和字符串参数数组。
shell结构:
main(user/sh.c:145): 用getcmd读一行输入;调用fork,创建shell进程的副本;父进程调用wait,子进程执行命令。
runcmd(user/sh.c:58):执行实际命令
exec(user/sh.c:78):执行指令。
shell在实现I/O重定向时利用了分离。
sbrk(n):提供更多的内存(malloc可能调用)。返回新内存的位置。
pid = wait ((int *) 0);
printf("child %d is done\n", pid);
} else if(pid == 0){
printf(