这是 MIT6.S081 的第一个 Lab ,目的是为了让学生熟悉 Xv6 和 Unix 的一些实用程序,例如 sleep
find
xargs
等。
sleep
这一关是实现 sleep
命令。由于在 user/user.h
目录中给出来了可以直接由 C 语言调用的函数 int sleep(int)
,这一关非常简单:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int sec;
if (argc != 2) {
fprintf(2, "Usage: sleep secends\n");
exit(1);
}
sec = atoi(argv[1]);
if (sec >= 0) {
sleep(sec);
} else {
fprintf(2, "Usage: sleep secends\n");
}
exit(0);
}
pingpong
这一关是为了让我们熟悉管道的使用,文档中强调了 uses UNIX system calls to ''ping-pong'' a byte between two processes over a pair of pipes, one for each direction
,所以我们应该创建两个管道,将父子进程的读写交叉连接:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
char buf[100];
// parent to child
int p2c[2];
// child to parent
int c2p[2];
pipe(p2c);
pipe(c2p);
if (fork()) {
// parent
close(p2c[0]);
close(c2p[1]);
write(p2c[1], "ping", 4);
read(c2p[0], buf, 4);
printf("%d: received pong\n", getpid());
close(p2c[1]);
close(c2p[0]);
} else {
// child
close(p2c[1]);
close(c2p[0]);
read(p2c[0], buf, 4);
printf("%d: received ping\n", getpid());
write(p2c[1], "pong", 4);
close(p2c[0]);
close(c2p[1]);
}
exit(0);
}
primes
这一关是使用多进程来实现筛法。本来一开始没懂他是什么意思,结果看到了文档中的一幅图就懂了。
写了个递归调用的函数,但是又不想再包装一层,所以当传入的文件描述符为 -1
时表示初始化:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define UPPER_BOUND 35
void send_and_close(int fd, int *num, int count) {
write(fd, num, sizeof(int) * count);
close(fd);
}
int receive_and_close(int fd, int *num) {
int res = read(fd, num, sizeof(int) * UPPER_BOUND);
close(fd);
return res / sizeof(int);
}
void sieve(int fd) {
int num[UPPER_BOUND];
int rec_count = 0;
// if fd < 0, init by building the array first
if (fd < 0) {
for (int i = 2; i <= UPPER_BOUND; ++i) {
num[rec_count++] = i;
}
} else {
rec_count = receive_and_close(fd, num);
}
if (rec_count <= 0) {
return;
}
int p[2];
pipe(p);
if (fork() > 0) {
// parent: send numbers
close(p[0]);
int filter_num = num[0];
printf("prime %d\n", filter_num);
int send_count = 0;
for (int i = 0; i < rec_count; ++i) {
if (num[i] % filter_num != 0) {
num[send_count++] = num[i];
}
}
send_and_close(p[1], num, send_count);
wait(0);
} else {
// child: receive numbers
close(p[1]);
sieve(p[0]);
}
}
int main(int argc, char *argv[]) {
sieve(-1);
exit(0);
}
find
文档中提示了可以参考 user/ls.c
,所以完全可以照着它抄抄改改,连函数 fmtname
都可以改写成 filename
复用。而且由于是改写的 ls
命令,所以错误处理就不用自己动手了。值得注意的是,在递归调用中要忽略掉 .
和 ..
目录,不然会有无限递归的情况。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
char *filename(char *path) {
static char buf[DIRSIZ + 1];
char *p;
for (p = path + strlen(path); p >= path && *p != '/'; --p)
;
p++;
if (strlen(p) >= DIRSIZ) {
return p;
}
memmove(buf, p, strlen(p));
buf[strlen(p)] = 0;
return buf;
}
void find(char *path, char *pattern) {
int fd;
if ((fd = open(path, 0)) < 0) {
fprintf(2, "find: cannot open %s\n", path);
return;
}
if (strcmp(filename(path), pattern) == 0) {
printf("%s\n", path);
}
struct stat st;
if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", path);
return;
}
char buf[512];
struct dirent de;
if (st.type == T_DIR) {
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: path too long\n");
} else {
strcpy(buf, path);
char *p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0) {
continue;
}
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) {
continue;
}
memmove(p, de.name, strlen(de.name));
p[strlen(de.name)] = 0;
find(buf, pattern);
}
}
}
close(fd);
}
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("find: wrong argumens\n");
}
find(argv[1], argv[2]);
exit(0);
}
当我删掉对 .
和 ..
目录的判断时,发现 Xv6 可以在崩溃后报错这是个无限递归,有时间可以研究一下是怎么实现的。我猜测是对栈和 call
指令进行了统计。
xargs
这一关是完成命令 xargs
,文档对这个实现的要求很低,不要求实现 -n
选项。所以可以直接将所有的 ' '
和 '\t'
换成 '\0'
,这样一来就可以复用缓冲区,直接将 cmd
数组的每个元素指向 buf
的某个位置即可。
#include "kernel/param.h"
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
char *cmd[MAXARG];
char buf[1024];
int cmd_cnt = 0;
for (int i = 1; i < argc; ++i) {
cmd[cmd_cnt++] = argv[i];
}
int len = read(0, buf, sizeof buf);
buf[len] = '\0';
if (len < 0) {
fprintf(2, "xargs: input error\n");
exit(1);
}
char *start = buf;
for (char *p = buf; *p != '\0'; ++p) {
if (*p == ' ' || *p == '\t') {
*p++ = '\0';
cmd[cmd_cnt++] = start;
start = p;
} else if (*p == '\n') {
*p++ = '\0';
cmd[cmd_cnt++] = start;
cmd[cmd_cnt] = 0;
start = p;
cmd_cnt = argc - 1;
if (fork() == 0) {
exec(cmd[0], cmd);
} else {
wait(0);
}
}
}
exit(0);
}