MIT-6.s081-OS lab util Unix utilities

实验说明原文:https://pdos.csail.mit.edu/6.828/2019/labs/util.html

第一个实验,按说是比较简单,但是做起来还是一波三折TAT

分为五个部分:

sleep

这个最简单,就是直接使用系统调用:

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

void usage(){
    printf("Usage: sleep  \n");
    printf("NUM must be non-negative\n");
    exit();
}


int main(int argc,char* argv[]){
    if(argc<2){
        usage();
    }
    char c=argv[1][0];
    if(c<'0'||c>'9'){
        usage();
    }
    // this atoi implementation stops when encounters non-digit
    int num=atoi(argv[1]);
    printf("sleep %d ticks\n",num);
    sleep(num);
    exit();
}

printf("sleep %d ticks\n",num) 这一句不是要求的,是测试时加上的,但是加上也不会算你错~

pingpong

这个是利用pipe,在父进程和子进程之间传递一个数据,因为只需要传递一次,所以也比较简单~

#include "user/user.h"

int main(){
    int parent_fd[2];
    int child_fd[2];
    pipe(parent_fd);
    pipe(child_fd);
    if(fork()==0){
        char p[8];
        read(parent_fd[0],p,8);
        printf("%d: received %s\n",getpid(),p);
        char c[8]="pong";
        write(child_fd[1],c,8);
    }else{
        char p[8]="ping";
        write(parent_fd[1],p,8);
        char c[8];
        read(child_fd[0],c,8);
        printf("%d: received %s\n",getpid(),c);
    }
    exit();
}

注意输出要和题目给出的实例一致,不然会报错~

primes

产生32以内的素数,普通做法是很简单的,但是这里要求使用csp style,因为对这个不熟悉,对使用fork的并发编程也不熟悉,所以搞了蛮久才搞懂:

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

#define R 0
#define W 1

int
main(int argc, char *argv[])
{
  int numbers[100], cnt = 0, i;
  int fd[2];
  for (i = 2; i <= 35; i++) {
    numbers[cnt++] = i;
  }
  // 注意fork是在这个循环内进行的
  while (cnt > 0) {
    pipe(fd);
    if (fork() == 0) {
      int prime, this_prime = 0;
      // 关闭写的一端
      close(fd[W]);
      cnt = -1;
      // 读的时候,如果父亲还没写,就会block
      while (read(fd[R], &prime, sizeof(prime)) != 0) {
          // 设置当前进程代表的素数,然后筛掉能被当前素数整除的数
        if (cnt == -1) {
          this_prime = prime;
          cnt = 0;
        } else {
            // 把筛出来的接着放在number数组里?不对,这里cnt是重新从0开始计数的
          if (prime % this_prime != 0) numbers[cnt++] = prime;
        }
      }
      // printf("pid %d ,prime %d\n",getpid(),this_prime);
        printf("prime %d\n",this_prime);
      // 关闭读
      close(fd[R]);
      // WARN 注意!这里子进程并没有结束!子进程接下来继续执行while循环(cnt>0
      // 然后接着fork,注意此时子进程的子进程会获得和子进程一样的cnt和numbers
      // 也就是筛过的,而不是原始的
    } else {
        // 父进程里
      close(fd[R]);
      for (i = 0; i < cnt; i++) {
        write(fd[W], &numbers[i], sizeof(numbers[0]));
      }
      close(fd[W]);
      wait();
      // 这个break,让父进程直接退出循环,从而结束了
      // 即父进程只是起了往第一个子进程传原始数据的作用

      break;
    }
  }
  exit();
}

find

这个不难,但是稍微复杂一点,参考ls.c可以知道find大致该怎么写,搞清楚几个结构的关系,也能实现基本的查找了

optional是实现正则匹配,这个可以在grep里参考,但是我这里没有实现

/*
    Usage: find path name
    实现在给定路径的文件夹树中找到全部匹配name的
*/
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
// 注意,这个返回的是一个定长的字符数组,所以如果实际长度不足,后面有一段是空白,如果输出这个或者用这个作比较,是不符合要求
char*
fmtname(char *path)
{
  // 这个函数的作用就是从一个文件的完整路径中取出文件名
  // 比如: ./hello.c -> hello.c
  static char buf[DIRSIZ+1];
  char *p;

  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;
  p++; 
  // Return blank-padded name.
  if(strlen(p) >= DIRSIZ)
    return p;
  memmove(buf, p, strlen(p));
  memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
  return buf;
}


void find(char* path,char *name){
    // printf("find (%s,,fmt(path):%s,fmtlen:%d,%s)\n",path,fmtname(path),strlen(fmtname(path)),name);
    char buf[512], *p=0;
    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_FILE:
        if(strcmp((path),name)==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);
        // 这里不用管原来的path有没有/,对于ls来说,有几个都一样
        *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("p: %s\tde.name:%s\tbuf:%s\tfmtbuf:%s\n",p,de.name,buf,fmtname(buf));
            if(strcmp(de.name,".")==0||strcmp(de.name,"..")==0){
                continue;
            }
            if(strcmp(de.name,name)==0){
                printf("%s/%s\n",path,name);
            }
            find(buf,name);
        }
        break;
    }
    close(fd);

}
int main(int argc,char* argv[]){
    // 这里为了简单,假定一定按照usage使用
    // 实际上如果只有一个参数,那么搜索路径为当前路径
    if(argc<3){
        exit();
    }
    find(argv[1],argv[2]);
    exit();

}

xargs

前面几个都算是比较熟悉的命令,但是这一个没有用过,所以也有点费劲,文档里说的是:

read lines from standard input and run a command for each line, supplying the line as arguments to the command

阮一峰的这篇博客写的比较形象:http://www.ruanyifeng.com/blog/2019/08/xargs-tutorial.html

实际上就是把标准输入转化为后面命令的命令行参数

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

#define LINE 128
#define PARAMS 10
int main(int argc, char *argv[])
{
  if (argc < 2)
  {
    exit();
  }
  printf("**********\n");
  for (int i = 0; i < argc; i++)
  {
    printf("%d %s\n", i, argv[i]);
  }
  printf("**************\n");
  char *cmd = argv[1];
  // line是作为参数使用的
  char line[LINE];
  // char *p=gets(line,LINE);
  char *params[PARAMS];
  int index = 0;
  // 例如:xargs echo hello,这样的,已经带了参数,就要把参数设置到
  // 最终执行时需要的参数数组里
  params[index++] = cmd;
  for (int i = 2; i < argc; i++)
  {
    params[index++] = argv[i];
  }

  int n = 0;
  while ((n = read(0, line, LINE)) > 0)
  {
    if (fork() == 0)
    {
      char *t = (char *)malloc(sizeof(char) * LINE);
      int c = 0;
      for (int i = 0; i < n; i++)
      {
        if (line[i] == '\n' || line[i] == ' ')
        {
          break;
        }
        t[c++] = line[i];
      }
      t[c] = '\0';
      params[index++] = t;
      printf("**********\n");
      for (int i = 0; i < index; i++)
      {
        printf("%d %s\n", i, params[i]);
      }
      printf("**************\n");

      exec(cmd, params);
      printf("exec fail!\n");
      exit();
    }
    else
    {
      wait();
    }
  }
  exit();
}

上面***中间的是debug信息,这里要注意的就是字符串的处理,注意exec的参数char **,结尾都是\0

所以第一个实验完结~

red@red-vm-ubuntu:~/6s081/xv6-riscv-fall19$ ./grade-lab-util 
make: 'kernel/kernel' is up to date.
sleep, no arguments: OK (1.0s) 
sleep, returns: OK (0.8s) 
sleep, makes syscall: OK (1.0s) 
pingpong: OK (0.9s) 
primes: OK (1.1s) 
find, in current directory: OK (1.2s) 
find, recursive: OK (1.5s) 
xargs: OK (1.1s) 
Score: 100/100

 

你可能感兴趣的:(操作系统,6.s081)