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 有一个实例: