xv6: a simple, Unix-like teaching operating system(Lab1)

33 阅读5分钟

lab地址

pdos.csail.mit.edu/6.S081/2022…

This lab will familiarize you with xv6 and its system calls.

sleep (easy)

题目大意:实现一个sleep程序,指定时间睡眠

代码如下:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argsc, char *argv[]) {
	if (argsc < 2) {
		fprintf(2, "Usage: sleep second\n");
		exit(0);
	}
	int s = atoi(argv[1]);
	sleep(s);
	exit(0);
}

pingpong (easy)

题目大意:根据管道写一个ping-pong程序,父进程发送ping,子进程打印received ping,并且返回pong,然后父进程打印received pong

代码如下:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void main(int argsc, char *argv[]) {
	int parent_fd[2], child_fd[2];
	pipe(parent_fd);
	pipe(child_fd);
	char buf[512];
	if (fork() == 0) {
		int pid = getpid();
		read(child_fd[0], buf, 4);
		printf("%d: received %s\n", pid, buf);
		write(parent_fd[1], "pong", 4);
		close(parent_fd[0]);
		close(parent_fd[1]);
		close(child_fd[0]);
		close(child_fd[1]);
	} else {
		int pid = getpid();
		write(child_fd[1], "ping", 4);
		read(parent_fd[0], buf, 4);
		printf("%d: received %s\n", pid, buf);
		close(parent_fd[0]);
		close(parent_fd[1]);
		close(child_fd[0]);
		close(child_fd[1]);	
	}
}

primes (moderate)/(hard)

题目大意: 需要打印2-35之间的质数,打印的方式如下

  • 第一个进程A在管道中填充2-35的数,之后他的子进程拿到2-35的数
  • A的子进程B拿出父进程的数,打印2,然后剔除掉2的倍数,再把剩余的数写入管道,即3,5,7,9...35
  • B的子进程C拿出父进程的数,打印3,然后剔除掉3的倍数,再把剩余的数写入管道,即5,7,11...35
  • 一直持续到所有数都被打印

image.png

代码如下:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

void main(int argsc, char *argv[]) {
    int p[2];
    pipe(p);
	// 首先把2-35填充进管道
	for (int i = 2; i <= 35; i++) {
		write(p[1], &i, sizeof(int));
	}
	close(p[1]);
	f(p[0]);
}

void f(int read_fd) {
	int p[2];
	pipe(p);
	int prime;
	if (!read(read_fd, &prime, sizeof(int))) {
		exit(0);
	}
	printf("prime %d\n", prime);
	if (fork() == 0) {
		close(p[0]);
		int cur;
		while (read(read_fd, &cur, sizeof(int))) {
			if (cur % prime != 0) {
				write(p[1], &cur, sizeof(int));
			}
		}
		exit(0);
	 } else {
	    close(p[1]);
		wait(0);
		f(p[0]);
	}
}

find (moderate)

实现find方法,比如示例find . b 表示找到当前目录下面所有名字为b的文件 image.png

提示让我们读ls.c的源码并且使用递归,别说,还真挺有道理,如果我能用ls找到当前目录下所有符合名字文件,用递归找到子目录的下符合名字的文件

那么问题来了,ls.c的源码晦涩难懂

核心源码如下

void
ls(char *path)
{
  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_DEVICE:
  case T_FILE:
    printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
    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;
      memmove(p, de.name, DIRSIZ);
      p[DIRSIZ] = 0;
      if(stat(buf, &st) < 0){
        printf("ls: cannot stat %s\n", buf);
        continue;
      }
      printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
    }
    break;
  }
  close(fd);
}

到26行之前应该都能看懂,26行是重头戏

如果字符串路径长度 + DIRSIZ(我理解就是文件名字,因为后面还得加个名字) > 缓冲区长度,报错

将path拷贝到buf中,然后将p指向buf的末尾并且加上/

循环把fd读到de中,如果没有数据了就跳出循环

如果de.inum==0,表示该文件没用,跳过

对读取到的文件,把名字追加到p的后面,再加个结束符

针对现在的p就是最新遍历到的文件,如果把读取到stat中失败就报错,成功就打印文件相关信息

大概就是这样,不知道为什么c语言处理字符串这么复杂,怪我大一没学好,OK,接下来在ls的模板上改一改就行了

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

void find(char *path, char *target);

void printTarget(char *path, char *target);

void main(int argsc, char *argv[]) {
	if(argsc < 2){
		find(".", argv[2]);
		exit(0);
	}
	find(argv[1], argv[2]);
	exit(0);
}

// 寻找path下面叫做target的文件
void find(char *path, char *target) {
  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_DEVICE:
  case T_FILE:
    printTarget(path, target);
    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 || (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)) {
        continue;
      }
      memmove(p, de.name, DIRSIZ);
      p[DIRSIZ] = 0;
      if(stat(buf, &st) < 0){
        printf("ls: cannot stat %s\n", buf);
        continue;
      }
      // printf("%s %d %d %d\n", buf, st.type, st.ino, st.size);
      find(buf, target);
    }
    break;
  }
  close(fd);
}


void printTarget(char *path, char *target) {
  char *p;
  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--) ;
  p++;
  if(strcmp(p, target) == 0) {
    printf(path);
    printf("\n");
  }
} 

xargs (moderate)

题目是什么意思呢,举个例子

echo hello too | xargs echo bye等价于echo bye hello too

也就是说

左边的输出,会当做右边的输入

比如find . b | xargs grep hello 假设find . b的输出为

./a/b
./c/b
./b

那么我们等价于下面三行

grep hello ./a/b

grep hello ./c/b

grep hello ./b

接下来用代码实现,大致逻辑如下,用文件描述符0接收输入,遇到\n执行exec命令

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"


void main(int argsc, char *argv[]) {
    int max = 1024;
    // 定义一个参数数组,先把xargs的参数存下来
    char *param[max];
    char *p;
    int paramCur = 0;
    for (int i = 1; i < argsc; i++) {
        p = argv[i];
        param[paramCur++] = p;
    }
    // 开始存标准输入的参数
    char buf[max]; 
    char temp[max];
    int r = 0;
    int l = 0;
    int i = 0;
    // 读取标准输入到buf中
    while (read(0, &buf[i], 1) > 0) {
        if (buf[i] == '\n') {
            temp[r] = '\0';
            if (fork() > 0) {
                wait(0);
            } else {
                param[paramCur] = &temp[l]; 
                // 子进程执行命令,注意这个param[0]得是cmd
                exec(argv[1], param);
                exit(0);
            }
            l = r + 1;
        } else {
            temp[r] = buf[i];
        }
        r++;
    }
    exit(0);
}