正在學習MIT的6.S081,把做的實驗寫一寫吧。
實驗的代碼放在了Github上。
第一個實驗是Lab util,算是一個熱身的實驗,沒有涉及到系統的底層,就是使用系統調用來完成幾個用戶模式的小程序。
Boot xv6 (easy)
啟動XV6,按照文檔執行就ok了。
$ git clone git://g.csail.mit.edu/xv6-labs-2020
$ cd xv6-labs-2020
$ git checkout util
$ make qemu
在XV6中沒有ps命令,而是使用Ctrl+p來查看正在運行的進程。
sleep (easy)
這一個就是仿照已有的程序調用一下sleep的系統調用就行了。
在Unix系統里面,默認情況下0
代表stdin
,1
代表stdout
,2
代表stderr
。這3個文件描述符在進程創建時就已經打開了的(從父進程復制過來的),可以直接使用。而分配文件描述符的時候是從當前未使用的最小的值來分配,因此可以關閉某個文件描述符再通過open
或dup
將該文件描述符分配給其他文件或pipe來實現輸入輸出重定向。
有一個小坑就是程序執行結束后要用exit(0)
來退出,而不是return 0
。
#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 [time]\n");
exit(1);
}
int time = atoi(argv[1]);
sleep(time);
exit(0);
}
pingpong (easy)
這一個實驗就是用pipe
打開兩個管道,然后fork
出一個子進程,完成要求的操作就行了。
fork
函數是一次調用兩次返回的函數,在父進程中返回子進程的pid,在子進程中返回0。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[]){
int p2c[2];
int c2p[2];
if(pipe(p2c) < 0){
printf("pipe");
exit(-1);
}
if(pipe(c2p) < 0){
printf("pipe");
exit(-1);
}
int pid = fork();
if(pid == 0){
// child
char buf[10];
read(p2c[0], buf, 10);
printf("%d: received ping\n", getpid());
write(c2p[1], "o", 2);
}else if(pid > 0){
// parent
write(p2c[1], "p", 2);
char buf[10];
read(c2p[0], buf, 10);
printf("%d: received pong\n", getpid());
}
close(p2c[0]);
close(p2c[1]);
close(c2p[0]);
close(c2p[1]);
exit(0);
}
primes (moderate)/(hard)
這一個是實現管道的發明者Doug McIlroy提出的計算素數的方法,該方法類似於篩法,不過是用管道和多線程實現的。
在main
函數中父進程創建了一個管道,輸入2~35。之后通過prime
函數來實現輸出,如果讀取到了超過兩個的數,就創建一個新進程來進行后續處理。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void prime(int rd){
int n;
read(rd, &n, 4);
printf("prime %d\n", n);
int created = 0;
int p[2];
int num;
while(read(rd, &num, 4) != 0){
if(created == 0){
pipe(p);
created = 1;
int pid = fork();
if(pid == 0){
close(p[1]);
prime(p[0]);
return;
}else{
close(p[0]);
}
}
if(num % n != 0){
write(p[1], &num, 4);
}
}
close(rd);
close(p[1]);
wait(0);
}
int
main(int argc, char *argv[]){
int p[2];
pipe(p);
int pid = fork();
if(pid != 0){
// first
close(p[0]);
for(int i = 2; i <= 35; i++){
write(p[1], &i, 4);
}
close(p[1]);
wait(0);
}else{
close(p[1]);
prime(p[0]);
close(p[0]);
}
exit(0);
}
find (moderate)
這一個就是仿照ls.c
中的方法,對當前目錄排除掉.
和..
后進行遞歸遍歷,同時對路徑名進行匹配,匹配到了就輸出。不過庫中沒有提供strstr
函數,只能自己寫了個\(O(n^2)\)的子串匹配算法。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void match(const char* path, const char* name){
//printf("%s %s", path, name);
int pp = 0;
int pa = 0;
while(path[pp] != 0){
pa = 0;
int np = pp;
while(name[pa] != 0){
if (name[pa] == path[np]){
pa++;
np++;
}
else
break;
}
if(name[pa] == 0){
printf("%s\n", path);
return;
}
pp++;
}
}
void find(char *path, char *name){
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
// printf("%s %d %d %l\n", path, st.type, st.ino, st.size);
match(path, name);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
if(de.name[0] == '.' && de.name[1] == 0) continue;
if(de.name[0] == '.' && de.name[1] == '.' && de.name[2] == 0) continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
find(buf, name);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[]){
if (argc < 3){
printf("Usage: find [path] [filename]\n");
exit(-1);
}
find(argv[1], argv[2]);
exit(0);
}
xargs (moderate)
這一個就是從輸入中構造出新的argc數組,然后用fork
和exec
執行就行了。大部分時間都用在輸入的處理上面了。。庫里面沒有提供readline
函數和split
,只能自己寫。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
char* readline() {
char* buf = malloc(100);
char* p = buf;
while(read(0, p, 1) != 0){
if(*p == '\n' || *p == '\0'){
*p = '\0';
return buf;
}
p++;
}
if(p != buf) return buf;
free(buf);
return 0;
}
int
main(int argc, char *argv[]){
if(argc < 2) {
printf("Usage: xargs [command]\n");
exit(-1);
}
char* l;
argv++;
char* nargv[16];
char** pna = nargv;
char** pa = argv;
while(*pa != 0){
*pna = *pa;
pna++;
pa++;
}
while((l = readline()) != 0){
//printf("%s\n", l);
char* p = l;
char* buf = malloc(36);
char* bh = buf;
int nargc = argc - 1;
while(*p != 0){
if(*p == ' ' && buf != bh){
*bh = 0;
nargv[nargc] = buf;
buf = malloc(36);
bh = buf;
nargc++;
}else{
*bh = *p;
bh++;
}
p++;
}
if(buf != bh){
nargv[nargc] = buf;
nargc++;
}
nargv[nargc] = 0;
free(l);
int pid = fork();
if(pid == 0){
// printf("%s %s\n", nargv[0], nargv[1]);
exec(nargv[0], nargv);
}else{
wait(0);
}
}
exit(0);
}