Lab: Xv6 and Unix utilities
Boot xv6
第一个Lab的首个关卡,就是配置实验所需环境并成功启动xv6。
环境配置参考:Tools Used in 6.S081
使用git下载源码:
$ git clone git://g.csail.mit.edu/xv6-labs-2021
Cloning into 'xv6-labs-2021'...
...
$ cd xv6-labs-2021
$ git checkout util
Branch 'util' set up to track remote branch 'util' from 'origin'.
Switched to a new branch 'util'
这里也给自己新建一个仓库,记录所有Lab的完成情况:
$ git remote rename origin old-origin
$ git remote add origin <新的仓库的URL>
$ git push -u origin util
这样在自己的仓库也有了util分支,实验预备代码都在old-origin下,完成情况记录到origin中。
进入实验目录中使用下面命令,编译运行xv6操作系统
$ make qemu
使用make grade可以检测每一关的完成情况。
sleep
完成sleep用户程序,很简单只需要调系统接口就行(#include顺序不能随意置换,遵循c构建逻辑)
#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 seconds\n");
exit(1);
}
int time = atoi(argv[1]);
sleep(time);
exit(0);
}
Makefile文件UPROGS中添加
UPROGS=\
...
$U/_find\
pingpong
这关就是学习使用pipe进行进程间通信,代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int pid;
int p[2]; /* pipe io */
pipe(p);
if (fork() == 0)
{
/* child */
int pid = getpid();
char buf[2];
if (read(p[0], buf, 1) != 1)
{
fprintf(2, "child: read failed\n");
exit(1);
}
close(p[0]);
printf("%d: received ping\n", pid);
if (write(p[1], buf, 1) != 1)
{
fprintf(2, "child: write failed\n");
exit(1);
}
close(p[1]);
exit(0);
}
else
{
/* parent */
pid = getpid();
char message[2] = "a";
char buf[2];
buf[1] = 0;
if (write(p[1], message, 1) != 1)
{
fprintf(2, "parent: write failed\n");
exit(1);
}
// wait for child
close(p[1]);
wait(0);
if(read(p[0], buf, 1) != 1){
fprintf(2, "parent: read failed\n");
exit(1);
}
printf("%d: received pong\n", pid);
close(p[0]);
exit(0);
}
}
Makefile中添加
$U/_pingpong\
primes
实现一个多进程的质数筛,进程间使用pipe通信,虽然是hard难度,但是只要理解了这张图就会很清晰:
根进程,也就是主进程,创建一个子进程,从2开始向该子进程分发每个整数,子进程使用自己接收的第一个整数作为prime的base,对其余每个整数作模运算,并筛掉结果为0的,其余整数输出给自己的子进程。
每个子进程都为自己创建一个子进程执行上述逻辑,同时wait创建的子进程,以实现并发控制。
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
__attribute__((noreturn)) void
new_proc(int p[2])
{
int base_prime;
int flag;
int n;
close(p[1]); /* only read */
if (read(p[0], &base_prime, 4) != 4)
{
fprintf(2, "process failed to read from the pipe\n");
exit(1);
}
printf("prime %d\n", base_prime);
flag = read(p[0], &n, 4);
if (flag)
{
/* read success */
int new_pipe[2];
pipe(new_pipe);
if (fork() == 0)
{
/* grandson */
new_proc(new_pipe);
}
else
{
close(new_pipe[0]);
if (n % base_prime)
write(new_pipe[1], &n, 4);
while (read(p[0], &n, 4))
{
if (n % base_prime)
write(new_pipe[1], &n, 4);
}
close(p[0]); /* close read pipe from parent*/
close(new_pipe[1]); /* close write pipe for child */
wait(0);
}
}
exit(0);
}
int main(int argc, const char *argv[])
{
int p[2];
pipe(p);
if (fork() == 0)
{
/* child */
new_proc(p);
}
else
{
/* parent */
close(p[0]); /* only write */
for (int i = 2; i <= 35; i++)
{
if (write(p[1], &i, 4) != 4)
{
fprintf(2, "first process failed to write %d into the pipe\n", i);
exit(1);
}
}
close(p[1]);
wait(0);
exit(0);
}
return 0;
}
Makefile中添加
$U/_primes\
find
实现find用户程序,其中的系统调用使用可以参考ls
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
// Regexp matcher from Kernighan & Pike,
// The Practice of Programming, Chapter 9.
int matchhere(char*, char*);
int matchstar(int, char*, char*);
int
match(char *re, char *text)
{
if(re[0] == '^')
return matchhere(re+1, text);
do{ // must look at empty string
if(matchhere(re, text))
return 1;
}while(*text++ != '\0');
return 0;
}
// matchhere: search for re at beginning of text
int matchhere(char *re, char *text)
{
if(re[0] == '\0')
return 1;
if(re[1] == '*')
return matchstar(re[0], re+2, text);
if(re[0] == '$' && re[1] == '\0')
return *text == '\0';
if(*text!='\0' && (re[0]=='.' || re[0]==*text))
return matchhere(re+1, text+1);
return 0;
}
// matchstar: search for c*re at beginning of text
int matchstar(int c, char *re, char *text)
{
do{ // a * matches zero or more instances
if(matchhere(re, text))
return 1;
}while(*text!='\0' && (*text++==c || c=='.'));
return 0;
}
void find_helper(char *path, char *target){
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);
exit(1);
}
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
exit(1);
}
switch(st.type){
case T_FILE:
// fprintf(2,"Usage: find <path> <target>\n");
if(match(target, path)){
printf("%s\n", path);
}
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
/* path长度 + '/' + DIRSIZE + '\0' 大于 buf长度*/
printf("find: 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;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
if(st.type == T_FILE && strcmp(de.name, target) == 0){
printf("%s\n", buf);
}
if(st.type == T_DIR && strcmp(de.name, ".") != 0 && strcmp(de.name, "..") != 0){
find_helper(buf, target);
}
}
break;
}
}
int main(int argc, char *argv[]){
if(argc != 3){
fprintf(2, "Usage: find <path> <target>\n");
exit(1);
}
find_helper(argv[1], argv[2]);
exit(0);
}
Makefile中添加
$U/_find\
xargs
xargs命令行工具:wangchujiang.com/linux-comma…
为了实现xargs需要对输入作处理,分行并按空格划分每一个指令的参数集合,代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
void copy(char **p1, char *p2)
{
*p1 = malloc(strlen(p2) + 1);
strcpy(*p1, p2);
}
// i - 起始下标
int readLine(char **pars, int i)
{
int d = 1024;
char buf[d];
int j = 0;
// 读取一行
while (read(0, buf + j, 1))
{
/* 遇到换行符 */
if (buf[j] == '\n')
{
buf[j] = 0;
break;
}
j++;
if (j == d)
{
fprintf(2, "Parameters too long in one line\n");
exit(1);
}
}
if (j == 0)
{
/* 没有读取到内容 */
return -1;
}
/* 以空格划分 */
int k = 0;
while (k < j)
{
if (i > MAXARG)
{
fprintf(2, "Too many parameters\n");
exit(1);
}
while ((k < j) && (buf[k] == ' '))
{
/* 忽略 如' abc abc'中的多个空格 */
k++;
}
int l = k; /* 起始位置 */
while ((k < j) && (buf[k] != ' '))
{
/* 保留字符串 */
k++;
}
buf[k++] = 0;
copy(&pars[i], buf + l);
i++;
}
return i;
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
fprintf(2, "usage: xargs command [args]\n");
exit(1);
}
else
{
int i;
char *pars[MAXARG]; /* 参数字符串数组 */
for (i = 1; i < argc; i++)
{
copy(&pars[i - 1], argv[i]);
}
int end;
while ((end = readLine(pars, argc - 1)) != -1)
{
pars[end] = 0;
if (fork() == 0)
{
exec(pars[0], pars);
exit(1);
}
else
{
wait(0);
}
}
}
exit(0);
}
Makefile中添加
$U/_xargs\
Optional challenge exercises(可选的额外练习)
这里有2个,uptime 和 resolve find issues,目前只做了前一个
uptime
非常简单:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
printf("%d\n",uptime());
exit(0);
}
Makefile中添加
$U/_uptime\
resolve find issues
为find解决一个直接匹配文件名的小问题,这里抄一下grep.c的match函数给find用,已包含在上方的find中。
resolve shell issues TODO
基本完成所有实验,使用 make grade 验证
Score: 99/100