前言
第一个实验是在XV6系统上实现五个实用程序。虽然不难,但是我写了好久……
实验
sleep
sleep本身不需要实现,只需要运行即可。
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "kernel/types.h" #include "user.h"
int main(int argc,char* argv[]){ if(argc != 2){ printf("Sleep needs one argument!\n"); exit(-1); } int ticks = atoi(argv[1]); sleep(ticks); printf("(nothing happens for a little while)\n"); exit(0); }
|
编译运行
- 修改Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| UPROGS=\ $U/_cat\ $U/_echo\ $U/_forktest\ $U/_grep\ $U/_init\ $U/_kill\ $U/_ln\ $U/_ls\ $U/_mkdir\ $U/_rm\ $U/_sh\ $U/_stressfs\ $U/_usertests\ $U/_grind\ $U/_wc\ $U/_zombie\ $U/_sleep\
|
- 运行

pingpong
要求
实现两个进程在管道两侧来回通信。
父进程将”ping”写入管道,子进程从管道将其读出并打印。子进程从父进程收到字符串后,将”pong“写入另一个管道,然后由父进程从该管道读取并打印。
原理
通过定义两个管道fp和sp,实现父子进程传输消息
注意,进程通常只持有某个管道的读出端或者写入端,因此使用的时候需要将另一端关闭。
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include "kernel/types.h" #include "user.h" int main(int argc,char* argv[]) { int fp[2],sp[2]; pipe(fp); pipe(sp); printf("program start!\n"); int pid = fork(); if(pid<0) { printf("error!"); } else if(pid == 0) { char *buffer = " "; close(fp[1]); read(fp[0], buffer, 4); printf("%d: received %s\n",getpid(),buffer); close(fp[0]); char *intput = "pong"; close(sp[0]); write(sp[1], intput, 4); close(sp[1]);
} else{ char *buffer = "ping"; close(fp[0]); write(fp[1], buffer, 4); close(fp[1]); close(sp[1]); read(sp[0], buffer, 4); printf("%d: received %s\n",getpid(),buffer); close(sp[0]); } exit(0); }
|
运行结果

primes
要求
使用管道实现“质数筛选”, 输出2~35之间的而所有质数。
原理
筛选思路:
- 主进程将所有数据输入到管道的左侧
- 子进程从管道读出并筛选出第一个数字x,排除掉x的倍数,其他数字再写入下一管道;
- 重复步骤二,直到管道中没有数字

源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include "kernel/types.h" #include "user.h" #include <stddef.h> void mapping(int n, int fd[]) { close(n); dup(fd[n]); close(fd[0]); close(fd[1]); } void primes() { int fd[2]; pipe(fd); int prime; int ref = read(0, &prime, sizeof(int)); if(ref == 0)return; printf("prime %d\n", prime); int pid = fork(); if(pid == 0) { int num; mapping(1, fd); while(read(0,&num, sizeof(int))) { if(num%prime == 0)continue; write(1, &num, sizeof(int)); } } else { wait(NULL); mapping(0, fd); primes(); } } int main(int argc,char* argv[]) { int fd[2]; pipe(fd); printf("program start!\n"); int pid = fork(); if(pid<0) { printf("error!"); } else if(pid == 0) { mapping(1,fd); for(int i = 2;i <= 35; i++) write(1, &i, sizeof(int));
} else{ wait(NULL); mapping(0, fd); primes(); } exit(0); }
|
运行结果

find
要求
在目录树中查找名称与字符串匹配的所有文件,输出文件的相对路径。
原理
借鉴ls指令的写法,读取路径下的文件名,如果是文件,就与寻找的文件名比较,如果是文件夹,就递归继续寻找该文件夹。
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #include "kernel/fs.h"
char* fmtname(char *path) { char *p;
for(p=path+strlen(path); p >= path && *p != '/'; p--) ; p++;
return p; }
void find(char *path, char *file_name) { char buf[512], *p; int fd; struct dirent de; struct stat st;
if((fd = open(path, 0)) < 0){ fprintf(2, "find: cannot open %s\n", path); return; }
if(fstat(fd, &st) < 0){ fprintf(2, "find: cannot stat %s\n", path); close(fd); return; }
if(st.type != T_DIR) { fprintf(2, "find: cannot find in %s\n", path); close(fd); return; } if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ return; }
strcpy(buf, path); p = buf+strlen(buf); *p++ = '/'; while(read(fd, &de, sizeof(de)) == sizeof(de)){ if(de.inum == 0) continue; memmove(p, de.name, DIRSIZ); p[DIRSIZ] = 0; if(stat(buf, &st) < 0){ printf("find: cannot stat %s\n", buf); continue; } char *name = fmtname(buf); switch (st.type) { case T_FILE: if(strcmp(name, file_name) == 0) { printf("%s\n", buf); } break; case T_DIR: if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; find(buf, file_name); break; default: break; } } close(fd); }
int main(int argc, char *argv[]) {
if(argc < 3){ printf("please input find [path] [file_name]\n"); exit(0); } find(argv[1], argv[2]); exit(0); }
|
运行结果

xargs
要求
从标准输入中读取行并 为每行运行一次 指定的命令,且将该行作为命令的参数提供。
原理
这个小实验主要是对于实现效果的理解。
|
命令行中的|会将左边的命令的输出通过管道传递到右侧,可以直接从标准输入中读取字符串。
exec函数
exec接收的二维参数数组argv,argv[0]必须是该命令本身,最后一个参数argv[size-1]必须为0,否则将执行失败。
1 2
| char *argv[]={"echo","hello",0}; exec(argv[0],argv);
|
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include "kernel/types.h" #include "user/user.h" #include "kernel/param.h"
int main(int argc, char* argv[]) { if(argc < 2){ printf("error inputs"); exit(0); } char *cmd = argv[1]; char *argvs[MAXARG]; for(int i=1;i<argc;i++) { argvs[i-1]=argv[i]; } char buff[1024]; while(read(0,&buff,sizeof(buff))) { int len = strlen(buff); argvs[argc-1]=&buff[0]; for(int i=0;i<len;i++) { if(buff[i]=='\n') { if(fork()==0) { buff[i]=0; argvs[argc]=0; exec(cmd,argvs); } wait(0); argvs[argc-1]=&buff[i+1]; } } } exit(0); }
|
运行结果
运行命令 xargstest.sh
1 2 3 4 5 6
| mkdir a echo hello > a/b mkdir c echo hello > c/b echo hello > b find . b | xargs grep hello
|

结语
第一个实验就耗费了大量的时间,有几个实验还借鉴了别人的写法。考虑到我不是科班出身,之后的实验不如趁早观看别人的写法,能学到东西就好。