# sleep

Implement a user-level sleep program for xv6, along the lines of the UNIX sleep command. Your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the file user/sleep.c .

可以使用系统提供的 sleep 函数,将命令行输入的字符串先转换为数字,再调用 sleep 执行。头文件 types.h 必须第一个包含,因为其中定义了一些变量类型,例如 int->uint。

#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("usage: sleep <trcks>!\n");
        exit(1);
    }
    int times = atoi(argv[1]);
    int ret = sleep(times);
    exit(ret);
}

最后在 makefile 中添加 sleep,make qemu 指令运行。

# pingpong

Write a user-level program that uses xv6 system calls to ‘‘ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file user/pingpong.c .

首先了解一下管道的用法:

fork () 函数可以创建子进程(可用父进程的 pid>0,子进程 pid=0 区分)。pipe () 函数可以创建一个管道,在 1 端写入,0 端读取,但不能同时被写和读,因此要实现父子进程的同时通信需要创建两个管道,在不使用管道时需要将相应的端口关闭防止堵塞。如上图所示,父子进程拥有相同的变量,因此两个进程中数组的 1 端都指向 pipe 写端,0 端都指向 pipe 读端,将不用的端口关闭,就能得到父进程写入,子进程读取的管道。

#include "kernel/types.h"
#include "user/user.h"
int main(int argc,char*argv[]){
    int p1[2],p2[2];
    pipe(p1),pipe(p2);
    //parent write on p1[1],child receive on p1[0]
    //child write on p2[1],parent receive on p2[0]
    int pid;
    char buf[]={'m'};// 任意字符
    int ret=fork();// 创建子进程
    if(ret==0){
        // 子进程
        pid=getpid();
        close(p1[1]);// 关闭自己用不到的端口防止堵塞
        close(p2[0]);
        read(p1[0],buf,1);// 如果没有数据会等待写入
        printf("%d: received ping\n", pid);
        write(p2[1],buf,1);
        exit(0);
    }else if(ret>0){
        // 父进程
        pid=getpid();
        close(p1[0]);
        close(p2[1]);
        write(p1[1],buf,1);
        read(p2[0],buf,1);
        printf("%d: received pong\n", pid);
        exit(0);
    }else{
        printf("pid value:%d error\n",ret);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        exit(1);
    }
}

# primes

Write a concurrent prime sieve program for xv6 using pipes and the design illustrated in the picture halfway down this page and the surrounding text. This idea is due to Doug McIlroy, inventor of Unix pipes. Your solution should be in the file user/primes.c .

本质是利用进程传递值,实现质数筛。

#include "kernel/types.h"
#include "user/user.h"
void process(int *p) {
  int prime_num;
  int len = read(p[0], &prime_num, 4);
  if (len == 0) {// 读完了
    close(p[0]);
    exit(0);
  } else {
    printf("prime %d\n", prime_num);
  }
  int num;
  int p_next[2];
  pipe(p_next);// 传递值的管道
  int pid = fork();// 创建一个子进程
  if (pid > 0) { // 父进程
    close(p_next[0]);
    while (read(p[0], &num, 4)) {// 筛选
      if (num % prime_num != 0) {
        write(p_next[1], &num, 4);// 写入要传递的值
      }
    }
    close(p[0]);
    close(p_next[1]);
    wait((int *)0);// 等子进程完成
    exit(0);
  } else if (pid == 0) { // 子进程
    close(p[0]);
    close(p_next[1]);
    process(p_next);// 递归处理
    close(p_next[0]);
    exit(0);
  } else {
    printf("pid:%d value error", pid);
    close(p[0]);
    close(p_next[0]);
    close(p_next[1]);
    exit(1);
  }
}
int main(int argc, char *argv[]) {
  int p[2];
  pipe(p);
  for (int i = 2; i <= 35; i++) {
    write(p[1], &i, 4);
  }
  int pid = fork();
  if (pid > 0) { // parent
    close(p[0]);
    close(p[1]);
    wait((int *)0);
    exit(0);
  } else if (pid == 0) { // child
    close(p[1]);
    process(p);
    close(p[0]);
    exit(0);
  } else { // error
    printf("pid:%d value error", pid);
    close(p[0]);
    close(p[1]);
    exit(1);
  }
}

# find

Write a simple version of the UNIX find program for xv6: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c .

参考 ls 的实现方法,其中部分数据结构的定义:

#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device
// the message of relative files
struct stat {
  int dev;     // File system's disk device
  uint ino;    // Inode number
  short type;  // Type of file
  short nlink; // Number of links to file
  uint64 size; // Size of file in bytes
};
struct dirent {
  // defind the file name and the length of file name
  // DIRSIZ means the max length of file
  ushort inum;
  char name[DIRSIZ];
};

思路:尝试打开路径,先将文件名拼接上去,过滤通配符,然后尝试打开拼接好的路径,如果是文件类型则输出路径,如果是目录类型则递归调用。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find(char *path, char *filename) {
  char buf[256], *p;
  int fd, fd1;
  struct dirent de;
  struct stat st, st1;
  if ((fd = open(path, 0)) < 0) {
    fprintf(2, "can not open %s\n", path);
    return;
  }
  if (fstat(fd, &st) < 0) {
    fprintf(2, "stat path %s failed\n", path);
    close(fd);
    return;
  }
  switch (st.type) {
  case T_FILE:
    fprintf(2, "path error\n");
    return;
  case T_DIR:
    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;
      }
      memcpy(p, de.name, DIRSIZ);
      if ((fd1 = open(buf, 0)) >= 0) {
        if (fstat(fd1, &st1) >= 0) {
          switch (st1.type) {
          case T_FILE:
            if (!strcmp(de.name, filename)) {
              printf("%s\n", buf);
            }
            close(fd1);
            break;
          case T_DIR:
            close(fd1);
            find(buf, filename); // 递归查找子目录
            break;
          default:
            close(fd1);
            break;
          }
        }
      }
    }
    break;
  }
  close(fd);
}
int main(int argc, char *argv[]) {
  if (argc != 3) {
    fprintf(2, "usage: [direction] [filename]\n");
    exit(0);
  }
  find(argv[1], argv[2]);
  exit(0);
}

# xargs

Write a simple version of the UNIX xargs program for xv6: its arguments describe a command to run, it reads lines from the standard input, and it runs the command for each line, appending the line to the command’s arguments. Your solution should be in the file user/xargs.c .

先将参数逐行保存,每次拼接一下再执行就行。

#include "kernel/types.h"
#include "kernel/param.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
  if (argc < 2) {
    fprintf(2, "Usage: xargs [command]\n");
    exit(1);
  }
  char *args[MAXARG + 1];
  int i = 0;
  for (int j = 1; j < argc; ++j) {
    args[i++] = argv[j];// 保存参数
  }
  char buf[512];
  char *p = buf;
  while (read(0, p, 1) == 1) {// 每次读取一个 char
    if ((*p) == '\n') {// 一行结束,执行一次
      *p = 0;// 字符串结尾
      if (fork()== 0) {
        args[i] = buf;
        exec(argv[1], args);// 每次执行的参数为 arg
        fprintf(2, "exec %s failed\n", argv[1]);
        exit(0);
      }
      wait((int*)0);
      p = buf;// 从头开始读入
    } else {
      p++;
    }
  }
  exit(0);
}

# Test

更新于