MIT 6.S081 操作系统课程系列1 Utilities


课程主页

https://pdos.csail.mit.edu/6.828/2020/schedule.html

课程目的

  1. 理解操作系统的设计和实现

  2. 能动手扩展一个小型os

  3. 能动手实现系统应用

这是mit的一套本科课程,希望能快速过一遍,扩展知识,查漏补缺。

RISC-V

  • RISC(Reduced Instruction Set Computer)精简指令集计算机。

  • CISC(Complex Instruction Set Computer)复杂指令集计算机。

  • 可参考 https://zhuanlan.zhihu.com/p/49176102 先有复杂指令集。越来越庞大、暴露各种缺点,就有了精简指令集。 risc不断进化,到了第五代,就是RISC-V。 RISC-V是开源的。人们可以在它基础上设计开发cpu。

xv6

  • https://pdos.csail.mit.edu/6.828/2020/xv6.html

  • xv6是mit开发的一个教学级别的操作系统,受Unix V6影响。作者都是业界大佬级别,质量有保证。

  • 本课程就用这个操作系统,会深入其代码。一共就五十几个代码文件。

  • 因为linux目前代码量巨大,很多层面上无从下手,那么从一个小而精的系统入手也是个很好的选择。 完成后希望能掌握一个os核心的框架。

  • xv6的手册:https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf

环境安装

  • https://pdos.csail.mit.edu/6.828/2020/tools.html

  • 虚拟机里起ubuntu20.04

  • 安装调试和模拟工具 sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu

  • 下载课程代码 git clone git://g.csail.mit.edu/xv6-labs-2020

实验流程

  • 切换到此次lab的分支git checkout util。 本次实验需要实现几个常用的shell命令。 需要参考user文件夹下已实现的命令。

1. sleep

  • 判断一下参数。把第一个参数转成int。再调sleep(system call)即可。

  • 改好代码后运行make qemu即可编译并运行xv6内核。如果编译有错会报错并停止。 内核运行起来后就会进入命令行界面,跟一般linux相似,但是很简陋,缺少各种功能。 ctrl-a再按x退出内核。

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

int
main(int argc, char *argv[])
{
  
  if(argc != 2)
  {
    fprintf(2, "missing arg\n");
  }
  else
  {
    
    int ticks = atoi(argv[1]);
    fprintf(2, "sleep %d ticks\n", ticks);
    sleep(ticks);
  }
  
  exit(0);
}

2. pingpong

  • 起两个pipe。fork。父子进程收发一下消息即可。

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

int main(int argc, char *argv[])
{
    int p_read_pipe[2];
    int c_read_pipe[2];
    char buf;
    char data = 'G';

    int pid;

    if(pipe(p_read_pipe) == -1){
        fprintf(2, "p_read_pipe failed\n");
        exit(0);
    }

    if(pipe(c_read_pipe) == -1){
        fprintf(2, "c_read_pipe failed\n");
        exit(0);
    }

    pid = fork();

    if(pid == -1){
        fprintf(2, "fork failed\n");
        exit(0);
    }
    else if(pid == 0)
    {
        fprintf(2, "child\n");
        read(c_read_pipe[0], &buf, 1);
        fprintf(2, "%d: received ping\n", getpid());
        write(p_read_pipe[1], &data, 1);

        close(p_read_pipe[1]);
        close(c_read_pipe[0]);
    }
    else if(pid > 0)
    {
        fprintf(2, "parent\n");
        write(c_read_pipe[1], &data, 1);
        read(p_read_pipe[0], &buf, 1);

        close(c_read_pipe[1]);
        close(p_read_pipe[0]);
        fprintf(2, "%d: received pong\n", getpid());
        wait(0);
    }

    exit(0);
}

3. primes

4. find

  • 直接抄ls.c。把文件夹的分支改成递归find即可。

  • fmtname里把空格填充去掉可得到干净的文件名。

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

char*
fmtname(char *path)
{
  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));

  //if(padding)
  //  memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
  memset(buf+strlen(p), 0, DIRSIZ-strlen(p));
  return buf;
}

void find(char *path, char *name)
{
  //printf("find %s %s\n", path, name);
  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_FILE:
    //printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
    if(strcmp(name, fmtname(path)) == 0)
    {
      fprintf(1, "%s\n", path);
      //printf(0, "%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);
    *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("gg %s\n", buf);
      //printf("gg2 %s %d\n", fmtname(buf), strlen(fmtname(buf)));
      if(strcmp("..", fmtname(buf)) != 0 && strcmp(".", fmtname(buf)) != 0)
      {
        //printf("gg3 %s\n", fmtname(buf));
        //printf("%d\n", strcmp(".", fmtname(buf)));
        //printf("gg4 %d\n", strlen(fmtname(buf)));
        //printf("gg5 %d\n", strlen("."));
        find(buf, name);
      }
      //printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
    }
    break;
  }
  close(fd);
}


int
main(int argc, char *argv[])
{
  if(argc != 3){
    fprintf(2, "error arg\n");
    exit(0);
  }

  find(argv[1], argv[2]);

  exit(0);
}

5. xargs

  • 有很多细节。

  • 得把find.c的输出写到stdout的1。之前输入到stderr的2了,半天取不到数据。

  • 管道传参数时,xargs从stdin的0取数据。碰到空格或\n时形成一个参数。

  • fork一下,子进程里exec执行即可。

  • 拼装exec的argv时最后要有一项0。否则执行不了。

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

// sh < xargstest.sh
// find . b | xargs grep hello

int main(int argc, char *argv[])
{

    char wtf;

    char child_buf[256];
    memset(child_buf, 0, sizeof(child_buf));

    char *child_argv[4];
    child_argv[0] = argv[1];
    child_argv[1] = argv[2];
    child_argv[2] = child_buf;
    child_argv[3] = 0;

    while(read(0, &wtf, 1))
    {
        if(wtf != '\n' && wtf != ' ')
        {
            memset(child_buf + strlen(child_buf), wtf, 1);
            continue;
        }

        int pid;

        pid = fork();

        if(pid == -1){
            fprintf(2, "fork failed\n");
            exit(0);
        }
        else if(pid == 0) // child
        {
            //printf("1 %s\n", argv[1]);
            //printf("2 %s\n", child_argv[1]);
            //printf("3 %s\n", child_argv[2]);
            exec(argv[1], child_argv);
        }
        else if(pid > 0) // parent
        {
            wait(0);
        }

        memset(child_buf, 0, sizeof(child_buf));
    }

    exit(0);
}