Lab: Xv6 and Unix utilities

实验一要求通读:

  • xv6-book 第一章

第一章内容概要

fork 系统调用调用一次,然后分别在子 进程 和父进程中返回一次,子进程中返回 0,父进程中返回子进程的 PID

wait 系统调用用来阻塞并等待一个子进程退出,并将子进程的 PID 写入参数中。如果调用进程没有子进程,直接返回 -1.如果父进程对子进程的 PID 不感兴趣,可以直接传入 0

exec 可以用来使用一个二进制文件替换掉当前二进制映像,替换后从新的二进制映像的 main 函数开始运行,exec 允许向新的二进制映像中传递参数:

char *argv[3];
argv[0] = "echo";
argv[1] = "hello";
argv[2] = 0;
exec("/bin/echo", argv);
printf("exec error\n");

程序运行时会自动打开三个文件描述符:stdin(0), stdout(1), stderr(2)。因此下面的代码可以用来将数据从 stdin 拷贝到 stdout:

char buf[512];
int n;
while (1) {
   n = read(0, buf, sizeof buf);
   if (n == 0) break;
   if (n < 0) {
      fprintf(2, "read error\n");
      exit(1);
   }
   if (write(1, buf, n) != n) {
      fprintf(2, "write error\n");
      exit(1);
   }
}

write 返回值为读取到的字节数。close 可以用来关闭一个 fd,而在使用 open、pipe、dup 调用时,拿到的 fd 总是最小的那个,因此可以用来进行重定向:

char *argv[2];
argv[0] = "cat";
argv[1] = 0;
if(fork() == 0) {
   close(0);
   open("input.txt", O_RDONLY);
   exec("cat", argv);
}

首先将 stdin 关掉,然后再打开一个文件,这时此文件的 fd 就是 0。因而完成了重定向。

管道是一个小的内核缓冲区,此缓冲区暴露了一对文件描述符分别用来进行读和写(也就是说管道是半双工的),下面的代码演示了使用管道的向 wc 传递信息:

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0){
   // 将 p[0] 重定向到 stdin
   close(0);
   dup(p[0]);
   close(p[0]);
   close(p[1]);
   exec("/bin/wc", argv);
}else{
   // 关闭管道的读端
   close(p[0]);
   write(p[1], "hello world\n", 12);
   close(p[1]);
}

如果没有可用数据,read 会阻塞至有数据写入或写端被关闭,在第二种情况下,read 会返回 0

文件系统

启动 xv6

这个比较简单,跳过

实现 sleep

一个入门项目,主要目的应该是为了熟悉源码,kernel/sysproc.c 中实现的 sys_sleep 是没有参数的,不知道第一个参数证明传进去的,应该是修改了栈指针。系统定义了带有参数的 sleep,可以直接调用。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char** argv){
   if(argc < 2){
      fprintf(2, "usage: sleep NUMBER...\n");
      exit(1);
   }
   sleep(atoi(argv[1]));
   exit(0);
}

在 Makefile 中的 UPROGS 部分将 sleep 添加进去,调用 make GRADEFLAGS=sleep grade 可以查看测试

pingpong

练习管道。管道在 user/sh.c:100 有一个实例: