概述
實驗一主要涉及調用系統調用函數,這些系統調用函數基本都是符合POSIX規范的,和操作系統本身關系較小,在調試的時候也可以先把程序在外部的Linux下跑通然后再放進xv6中運行。
內容
sleep
基本沒什么特別的內容,按說明的做就行了。
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
int i;
if (argc < 2) {
printf("sleep: tick number is not specified\n");
exit(0);
}
i = atoi(argv[1]);
if (i <= 0) {
printf("sleep: tick number is invalid\n");
exit(0);
}
sleep(i);
exit(0);
}
pingpong
涉及進程創建和管道,當一個進程在read的時候會掛起直至讀到內容,所以進程的執行順序也是非常清晰的。
#include "kernel/types.h"
#include "user/user.h"
int main() {
int parent_fd[2], child_fd[2];
char buf[16];
pipe(parent_fd); pipe(child_fd);
if (fork() == 0) {
read(parent_fd[0], buf, 4);
buf[4] = '\0'; printf("%d: received %s\n", getpid(), buf);
write(child_fd[1], "pong", 4);
} else {
write(parent_fd[1], "ping", 4);
read(child_fd[0], buf, 4);
buf[4] = '\0'; printf("%d: received %s\n", getpid(), buf);
}
exit(0);
}
primes
這個算是管道比較高級的應用,有點流水線那味道。根據實驗文檔所給的參考鏈接里的圖片
給人一種每個進程都是篩完合數然后將篩完的數一股腦傳給下一級進程的感覺,其實不然,這樣一方面需要多一個緩沖區來存儲篩完的數,另一方面也不方便並行。實際上應該更好地利用管道的隊列性質。對於每個進程,從輸入管道得到第一個質數后就創建子進程,然后對輸入管道剩下的數進行篩選,如果合法就放入輸出管道,這樣多個進程同時操作,也不用緩沖區了。子進程中創建子進程這種操作很難直接表示,需要用到遞歸思想。
#include "kernel/types.h"
#include "user/user.h"
void f(int pp) {
int prime;
if (read(pp, (char *)&prime, 4) != 0) {
printf("prime %d\n", prime);
int p[2]; pipe(p);
if (fork() == 0) {
close(p[1]); f(p[0]);
} else {
int t;
while (read(pp, (char *)&t, 4) != 0) {
if (t % prime != 0) write(p[1], (char *)&t, 4);
}
close(p[1]); close(p[0]); close(pp); wait(0);
}
} else close(pp);
}
int main() {
int i, p[2]; pipe(p);
if (fork() == 0) {
close(p[1]); f(p[0]);
} else {
for (i = 2; i < 36; i++) write(p[1], (char *)&i, 4);
close(p[1]); close(p[0]); wait(0);
}
exit(0);
}
注意每次fork以后會給當前的管道添加一個引用,而想要關閉一個管道必須將它的所有引用都關閉才行。因此每次在fork之后關閉不用的管道是一個好習慣,防止忘關導致其他進程讀不到eof。
find
基本上套用ls.c里的內容。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
const char *now = ".", *par = "..";
char* fmtname(char *path) {
char *p;
for(p = path + strlen(path); p >= path && *p != '/'; p--);
p++;
return p;
}
void find(char *path, char *pattern) {
char buf[128], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
printf("find: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
printf("find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
if (strcmp(pattern, fmtname(path)) == 0) printf("%s\n", path);
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 (strcmp(de.name, now) == 0 || strcmp(de.name, par) == 0) continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf, pattern);
}
break;
}
close(fd);
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("find: argument is less than 2\n");
exit(0);
}
find(argv[1], argv[2]); exit(0);
}
xargs
也沒啥好說的,基本就是對命令行參數和標准輸入的字符串處理。
#include "kernel/types.h"
#include "kernel/param.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
char line[256], *p[MAXARG], ch;
int lines = 0, linen, ps = 0, pn, i, j;
for (i = 0; i < argc - 1; i++) {
p[ps++] = line + lines;
for (j = 0; j < strlen(argv[i + 1]); j++)
line[lines++] = argv[i + 1][j];
line[lines++] = '\0';
}
linen = lines; pn = ps; p[pn++] = line + linen;
while (read(0, &ch, 1) > 0) {
if (ch == '\n') {
line[linen++] = '\0'; p[pn++] = 0;
if (fork() == 0) exec(argv[1], p);
else {
wait(0); linen = lines; pn = ps; p[pn++] = line + linen;
}
} else if (ch == ' ') {
line[linen++] = '\0'; p[pn++] = line + linen;
} else line[linen++] = ch;
}
exit(0);
}