Bootstrap

XV6实验-Lab0 Utilities

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(
;