Chap.3 OS-挑战性任务shell

Chap.3 OS-挑战性任务shell

Toryn Lv1

实验内容

Shell 挑战性任务

  • 本次 Lab6 挑战性任务需要同学们在 MOS 原有的 shell(mosh) 上实现新的功能,该任务会提供若干任务,完成所有任务后即可参加自动化评测获取挑战性任务分数。
  • 实现不带.b后缀指令
  • 实现指令条件执行
  • 实现更多指令
  • 实现反引号
  • 实现注释功能
  • 实现历史指令
  • 实现一行多指令
  • 实现追加重定向
  • 实现引号支持
  • 实现前后台任务管理

功能实现详解

实现不带.b后缀指令

你需要实现不带.b后缀的指令,但仍需兼容带有 .b 后缀的指令,如 lsls.b 都应能够正确列出当前目录下的文件。

  • 对于实现不带.b的指令:
  • 指令都是通过sh.c中的spawn(prog, argv)来实现的
    • prog就是指向程序的字符串,比如"ls.b", "echo.b"
    • 因此如果prog"ls"或者"echo"等不带.b后缀的字符串的话
    • 就新建一个字符串
    • 然后把prog拷贝给prog_n
    • 然后给prog_n加上”.b”后缀
    • 然后fd = open(prog_n, O_RDONLY)就可以打开这个程序的二进制文件并且加载了
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      //////////////// 1. ls不带.b /////////////////
      char prog_n[1024];
      strcpy(prog_n, prog);
      int mylen1 = strlen(prog_n);
      char myendchar = prog_n[mylen1-1];
      if (myendchar != 'b') {
      prog_n[mylen1] = '.';
      prog_n[mylen1+1] = 'b';
      prog_n[mylen1+2] = '\0';
      }
      /////////////////////////////////////////////

      // Step 1: Open the file 'prog' (the path of the program).
      // Return the error if 'open' fails.
      int fd;
      if ((fd = open(prog_n, O_RDONLY)) < 0) {
      return fd;
      }

实现指令条件执行

你需要实现 Linux shell 中的 &&||。 对于 command1 && command2command2 被执行当且仅当 command1 返回 0;对于 command1 || command2command2 被执行当且仅当 command1 返回非 0 值。

注: 评测中保证不出现括号。并且需要注意的是,在 bash 中 &&|| 的优先级相同,按照从左到右的顺序求值。
例如 cmd1 || cmd2 && cmd3,若 cmd1 返回 0,则 cmd1 执行后 cmd2 不会被执行,cmd3 会被执行;若 cmd1 返回非 0 且 cmd2 返回非 0,则 cmd3 将不会被执行。
提示:你可能需要修改 MOS 中对用户进程 exit 的实现,使其能够返回值。

  • 首先要清楚shell的调用流程
    • 首先在user/init.cspwanl("sh.b","sh",NULL),开启了运行sh.b程序的进程

    • 然后sh.b中的main()函数负责不断从标准输入逐行读取输入的指令到char *buf

    • 然后使用runcmd(buf)来运行指令

    • runcmd(buf)中调用parsecmd(argv, &rightpipe)来解析指令

    • parsecmd(argv, &rightpipe)中不断循环调用gettoken()来解析命令

    • gettoken()实际上调用_gettoken()来判断每次解析到的单词是是什么类型

      • 一共有三种类型
        • 0: 代表解析解结束
        • t: 代表解析到特殊字符
          • 这里对于<,|,>,&,;,(,)直接返回ascii码就可以
          • 对于||&&,也选择在_gettoken()中完成
            • 具体方式是识别到|或者&的时候,再多走一步判断下一个字符是不是|或者&
            • 对于||,我们返回1
            • 对于&&,我们返回2
            • 因为在ascii码表中这两个值被保留
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              ///////////////// 2. 判断||和&& /////////////////
              if (t == '|' && (*s) == '|') {
              t = 1;
              *p1 = s;
              *s++ = 0;
              *p2 = s;
              } else if (t == '&' && (*s) == '&') {
              t = 2;
              *p1 = s;
              *s++ = 0;
              *p2 = s;
              } else if (t == '>' && (*s) == '>') {
              t = 3;
              *p1 = s;
              *s++ = 0;
              *p2 = s;
              }
              ///////////////////////////////////////////////
        • 'w': 代表解析到一个单词
    • 然后再parsecmd()中增加能解析指令的case情况

      • case 1: 代表读取到了||
      • case 2: 代表读取到了&&
  • 然后弄清楚管道的进程创建
    • 管道命令 ls | cat

    • 是由sh.bfork()出一个子进程

      • 子进程去进一步parsecmd(),从而spawn出cat命令
      • sh.b返回原进程,spawnls命令
      • 然后通过管道开关读写端实现ls把输出传递给cat
    • 条件指令可以采用一样的形式,只不是管道中,越靠前的指令由父进程创建,越靠后的指令由子进程创建

    • 条件指令中我们需要获取靠前指令的执行结果,并且返回给后面的指令,因为前面的指令要用子进程创建,然后子进程给父进程发送信息,父进程再决定后面的指令可不可以执行

    • 首先fork()出一个子进程,设置need_ipc_send,然后返回

      • spawn一个命令
      • 这个命令通过修改libos.c中的libmain,给子进程发送了直接结果的信息
      • 然后子进程通过if(child>0){ ipc_recv(0,0,0); }来等待spawn出的命令发送信息
      • 等到了之后,进入if(need_ipc_send)来给父进程发送信息
    • 然后回到parsecmd()case 2的父进程分支

      • 父进程在此处ipc_recv(0,0,0)等待子进程发送信息

      • 结合env->env_ipc_value和现在是case 1 还是case 2 来决定是否要跳过下一个指令,直到找到下一个 case 1 或者 case 2

        • 跳过的方法就是在case内部调用c = gettoken(0,&t) != 0, 循环直到有c == 1 或者 c == 2

        • 然后设置need_ipc_recv1,这样返回runcmd的时候,才会进入结合condi_or或者condi_and来判断是否可以spawn这个指令

        • 然后设置condi_or或者condi_and其中一个为1

        • 然后继续parsecmd()

        • 这个时候往下走一层

        • 如果这一层只有一条指令,就会直接在case 'w'中返回,最终指令由父进程创建并执行

        • 如果这一层有两条指令,就会进入case 2,然后fork()新的子进程去执行中间那条指令,父进程继续ipc_recv(0,0,0)等待

        • 而且由于跳过了不允许执行的指令,因此有子进程的spawn出来的都是可以执行的指令,这个时候这些指令执行的返回值==0或者!=0再由子进程发送给父进程

        • 父进程再决定下一层的指令是创建还是跳过

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          char *argv[MAXARGS];
          int rightpipe = 0;
          int need_ipc_send = 0;
          int need_ipc_recv = 0;
          int condi_or = 0;
          int condi_and = 0;
          int bg = 0;
          int backquote = 0;
          int backpipe = 0;
          int argc = parsecmd(argv, &rightpipe, &need_ipc_send, &need_ipc_recv, &condi_or, &condi_and, &bg, &backquote, &backpipe);
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        int child = -1;
        if (need_ipc_recv != 0) {
        if (condi_or && env->env_ipc_value != 0) {
        child = spawn(argv[0], argv);
        } else if (condi_and && env->env_ipc_value == 0) {
        child = spawn(argv[0], argv);
        }
        } else {
        child = spawn(argv[0], argv);
        // debugf("创建echo子进程:%x\n",child);
        if (bg == 2) {
        syscall_add_jobs(child,news);
        }
        }

实现更多指令

你需要实现 touch,mkdir,rm 指令,只需要考虑如下情形:

  • touch:

    touch <file>:创建空文件 file,若文件存在则放弃创建,正常退出无输出。 若创建文件的父目录不存在则输出 touch: cannot touch '<file>': No such file or directory。 例如 touch nonexistent/dir/a.txt 时应输出 touch: cannot touch 'nonexistent/dir/a.txt': No such file or directory

  • mkdir:

    mkdir <dir>:若目录已存在则输出 mkdir: cannot create directory '<dir>': File exists,若创建目录的父目录不存在则输出 mkdir: cannot create directory '<dir>': No such file or directory,否则正常创建目录。
    mkdir -p <dir>:当使用 -p 选项时忽略错误,若目录已存在则直接退出,若创建目录的父目录不存在则递归创建目录。

  • rm:

    rm <file>:若文件存在则删除 <file>,否则输出 rm: cannot remove '<file>': No such file or directory
    rm <dir>:命令行输出: rm: cannot remove '<dir>': Is a directory
    rm -r <dir>|<file>:若文件或文件夹存在则删除,否则输出 rm: cannot remove '<dir>|<file>': No such file or directory
    rm -rf <dir>|<file>:如果对应文件或文件夹存在则删除,否则直接退出。

  • touch

    • 先调用user/lib/file.c中的open(path, O_RDONLY)打开文件,如果打开成功就说明文件已经存在,退出
    • 如果打开失败,就再次调用open(path, O_CREAT)来创建这个文件,如果创建失败就打印提示信息
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      if (argc != 2)
      {
      debugf("touch:缺少文件名\n");
      return -1;
      }

      int fd = open(argv[1], O_RDONLY);
      if (fd >= 0) {
      debugf("touch:文件已存在\n");
      return -1;
      }

      fd = open(argv[1], O_CREAT);
      if (fd < 0) {
      printf("touch: cannot touch \'%s\': No such file or directory\n",argv[1]);
      return -1;
      }
      close(fd);
      return 0;
  • mkdir

    • 首先可能的指令类型有两种
      • mkdir x
      • mkdir -p x
      • -p表示忽略错误信息,包括目录已存在时不打印提示信息,路劲不存在时递归创建路径
    • 首先判断argv[1]是不是"-p\0"
      • 然后使用stat(path, &stat_buf)函数判断目录是否已经存在
        • 已经存在 + 无-p选项:打印提示信息
        • 已经存在 + -p选项:退出
      • 如果不存在
        • 先直接mkdir(path)

        • mkdir(path)函数是自己写的,在user/lib/file.c中,并且需要在user/include/lib.h中声明

        • 这个函数就是调用file.c中的open()函数open(path, O_MKDIR)了一下

          • 实际上是与文件系统服务进程通信,调用了fs/serv.c中的serve_open()函数
          • 首先模仿O_CREAT的方法,直接复制一份,然后改成O_MKDIR
          • 然后创建完成后,如果是O_MKDIR,就要把f->f_type赋值为FTYPE_DIR
        • 这样可以完成单层目录的创建

        • 因为serv.c中的serve_open()中调用了fs/fs.c的file_create()file_create()调用了walk_path()

        • walk_path()的原理是,如果路径中有一环不存在就会返回负值,因此文件系统服务进程自带的file_create()不能完成递归创建目录

        • 所以需要自己写一个descend_mkdir()函数

        • 该函数的递归出口是 fd = mkdir(path), 如果 fd >= 0 就是创建成功,说明此时在创建缺失路径的最顶层

        • 如果 fd < 0,就要去除最底层的目录,然后调用descend_mkdir()创建他的上一级目录,然后再重新调用descend_mkdir()创建自己

        • 最后根据有没有-p选项来选择在创建失败的时候是否要递归创建目录

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          void descend_mkdir(char *path) {
          int fd = mkdir(path);
          if (fd > 0) {
          return;
          }

          if (fd < 0) {
          // 1. find last '\'
          char newpath[1024];
          strcpy(newpath, path);
          int len = strlen(newpath);
          for(int i = len - 1;i >= 0;i--) {
          if(newpath[i] == 47) {
          newpath[i] = '\0';
          break;
          }
          }
          // 2. create father
          descend_mkdir(newpath);
          // 3. create self
          descend_mkdir(path);
          }
          }
  • rm

    • 首先根据可能的情况
      • rm x
      • rm -r x
      • rm -rf x
      • 判断是否有-r或者-rf,然后对没有选项,有-r选项,有-rf选项分别处理
    • -r选项
      • user/lib/file.c中的remove(path)函数实际上与文件系统服务进程进行通信,然后调用fs/fs.c中的file_remove()函数
      • 该函数可以删除文件,也可以删除目录
      • 因此直接调用remove(argv[2])即可实现递归删除
      • 如果删除失败则打印提示信息
        1
        2
        3
        4
        5
        6
        7
        if (isr) {
        r = remove(argv[2]);
        if (r < 0) {
        printf("rm: cannot remove \'%s\': No such file or directory\n",argv[2]);
        return -1;
        }
        }
    • -rf选项
      • 直接删除即可
      • 无论成功失败都不打印提示信息
        1
        2
        3
        4
        else if (isrf) {
        remove(argv[2]);
        return 0;
        }
    • 没有选项
      • 要先判断需要删除的是否是目录
      • 首先fd = open (path, O_RDONLY)打开该文件,获取文件描述符下标
      • 然后fd_lookup(fd, &fd_)寻找文件描述符,需要引入<fd.h>
      • 然后把&fd_强制转换为struct Filefd *ffd;
      • 然后获取ffd->f_file.f_type,判断ffd->f_file.f_type == FTYPE_DIR
      • 如果是目录就删除失败,打印提示信息
      • 否则就说明是文件类型,删除即可,删除失败打印提示信息
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        else {
        if (argc != 2) {
        debugf("rm:缺少文件名\n");
        return -1;
        }

        // struct Stat stat_buf;
        // if (stat(argv[1], &stat_buf) >= 0) {
        // printf("rm: cannot remove \'%s\': Is a directory\n", argv[1]);
        // return -1;
        // }

        int fd = open(argv[1],O_RDONLY);
        if (fd >= 0) {
        struct Fd *fd_;
        fd_lookup(fd, &fd_);
        struct Filefd *fdd = (struct Filefd*)fd_;
        if (fdd->f_file.f_type == FTYPE_DIR) {
        printf("rm: cannot remove \'%s\': Is a directory\n", argv[1]);
        return -1;
        }
        }

        r = remove(argv[1]);
        if (r < 0) {
        printf("rm: cannot remove \'%s\': No such file or directory\n",argv[1]);
        return -1;
        }
        }

实现反引号

你需要使用反引号实现指令替换。只需要考虑 echo 进行的输出,你需要将反引号内指令执行的所有标准输出替换为 echo 的参数。例如:
echo `ls | cat | cat | cat`

  • 识别反引号
  • 子进程执行反引号内指令
    • 传递管道
    • backquote而且rightpipe == 0的时候再复制dup(p_quote[1],1),把写端从stdout改成p_quote[1]
      1
      2
      3
      4
      5
      // parsecmd()中
      if ((r = fork()) == 0) {
      *backquote = 1;
      return parsecmd(argv, rightpipe, need_ipc_send, need_ipc_recv, condi_or, condi_and, bg, backquote, &p_quote[1]);
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // runcmd()中
      if (child >= 0) {
      /* code */
      /////////////////// backquote ///////////////////
      if (rightpipe == 0 && backquote == 1) {
      // 2. 管道
      dup(backpipe, 1);
      close(backpipe);
      close(backpipe);
      }
      /////////////////////////////////////////////////
      /* code */
      } else {
      /* code */
      }
  • 父进程读取执行结果
    • 父进程从dup(p_quote[0],0)把从stdin读改成从p_quote[0]
    • 读取的时候因为管道只有32字节
    • 因此需要使用while(1) { read(p_quote[0], tempbuf + i_buf, 1024 - i_buf); }一段一段的读取,不能使用read()一次性读完
    • 读完之后作为argv[argc++] = tempbuf加入到参数中
    • 最后还要使父进程的gettoken走到下一个'`'
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      else {
      // 1. 父进程读取执行结果,加入到argv
      close(p_quote[1]);
      char tempbuf[1024] = {0};
      int i_buf = 0;
      while (1) {
      int len = read(p_quote[0], tempbuf + i_buf, 1024 - i_buf);
      if (len <= 0) {
      break;
      }
      i_buf += len;
      }
      tempbuf[i_buf] = 0;
      close(p_quote[0]);
      // 2. 先等待子进程完成
      wait(r);
      argv[argc++] = tempbuf;
      // 3. 跳到下一个'`'
      while((c = gettoken(0, &t)) != '`') { }
      }

实现注释功能

你需要使用 # 实现注释功能,例如 ls | cat # this is a comment meowls | cat 会被正确执行,而后面的注释则会被抛弃。

  • parsecmd中,识别到case #直接结束指令解析并返回即可
    1
    2
    case '#':
    return argc;

实现历史指令

你需要实现 shell 中保存历史指令的功能,可以通过 UpDown 选择所保存的指令并执行。你需要将历史指令保存到根目录的 .mosh_history 文件中(一条指令一行),为了评测的方便,我们设定 $HISTFILESIZE=20(bash 中默认为 500),即在 .mosh_history 中至多保存最近的 20 条指令。你还需要支持通过 history 命令输出 .mosh_history 文件中的内容。

注:在 bash 中,historyshell built-in command,我们规定需要将 history 实现为 built-in command

你需要将当前执行的指令先存入 .mosh_history 中,例如:

echo `ls | cat`
echo meow # comment
history
history | cat

当历史指令为空时,依次执行上述四条指令后,后两条指令会分别输出

echo `ls | cat`
echo meow # comment
history

echo `ls | cat`
echo meow # comment
history
history | cat

使用 Up 能够切换到上一条指令(如果上一条指令存在),使用 Down 能够切换到下一条指令(如果下一条指令存在)。能够选择的指令范围为:用户当前输入的指令与 .mosh_history 文件中保存的所有指令。例如在执行了上述四条指令后,用户输入了 echo,此时 Up 应该将指令切换至 history | cat,再次进行三次 Up 后切换至 echo `ls | cat`,此时再次 Up 应保留在该指令(因为已经不存在上一条指令);再进行四次 Down 后将切换回 echo,此时再次 Down 应保留在该指令(因为不存在下一条指令)。

  • 使用内核实现历史指令,历史指令在内核中
  • 首先考虑history | cat的情况
    • 所有history的识别和执行要在parsecmd之后,这样有"|"的时候才会输出到p[1]
    • 而不是直接进以来就识别history
  • 然后所有的指令退出前要加入到历史指令中
    • exit()syscall_history_add()一下
    • 对于内建指令,更早exit(),也要更早history_add()
    • 对于ls | cat | cat这种指令,会在顶层 echo ... 123exit()之前history_add()
    • 因此只要是bakcquote == 1,那就不需要history_add()
  • 对于history指令本身
    • 根据需求,需要在打印历史指令之前就history_add()

实现一行多指令

你需要实现使用 ; 将多条指令隔开从而从左至右依顺序执行每条指令的功能。例如:
ls;ls | cat; echo nihao,mosh; echo `ls; echo meow`

  • ls ; ls
  • 第一个ls开一个子进程去执行; 第二个ls父进程执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    case ';':;
    int temp_r = 0;
    if ((temp_r = fork()) == 0) {
    return argc;
    } else {
    wait(temp_r);
    return parsecmd(argv, rightpipe, need_ipc_send, need_ipc_recv, condi_or, condi_and, bg, backquote, backpipe);
    }
    break;

实现追加重定向

你需要实现 shell 中 >> 追加重定向的功能,例如:
ls >> file1; ls >> file1
最后文件 file1 中将会有两次 ls 指令的输出。

  • 识别>>,由于ascii3被保留,这里返回3;在parsecmdcase 3 中做进一步处理
  • 然后找到>> file1后面这个字符串所指的文件
    • int temp_fd = open(t, O_APPEND | O_RDWR);
    • O_APPEND这个权限在fs的serv.c中的serve_open实现
      • 就是把ff->f_fd.fd_offset = f->f_size;
      • 识别到O_APPEND打开模式的时候,把文件描述符的偏移量改为文件大小,这样打开之后就自动定位在文件的末尾
    • 如果打开失败说明文件不存在
      • open(t, O_CREAT)创建这个文件
      • 然后open(t, O_APPEND | O_RDWR)再次打开
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        case 3:;
        // 1. 获取文件
        c = gettoken(0, &t);
        int fd_temp = open(t, O_APPEND | O_RDWR);
        if (fd_temp < 0) {
        fd_temp = open(t, O_CREAT);
        fd_temp = open(t, O_APPEND | O_RDWR);
        // debugf("两次打开文件%s\n",t);
        } else {
        // debugf("一次就打开文件%s\n",t);
        }
  • 然后把stdout改成指向这个文件,指完之后要关闭文件
    • 也就是dup(temp_fd, 1);
    • close(temp_fd);
      1
      2
      3
      4
      // 2. 修改输出路径
      dup(fd_temp, 1);
      close(fd_temp);
      break;

实现引号支持

你需要实现引号支持,比如 echo "ls >",shell 在解析时需要将双引号内的内容看作是单个字符串。

  • gettoken的时候,如果是识别到'\"'
    • 就直接一直读取到下一个'\"'
    • 期间内的字符串打包成'w'返回
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      if (*s == '\"') {
      *s++ = 0;
      *p1 = s;

      s++;
      while(*s != '\"') {
      s++;
      }

      *s++ = 0;
      *p2 = s;
      return 'w';
      }

实现前后台任务管理

  • 你需要支持 mosh 运行后台进程,当命令的末尾添加上 & 符号时,该命令应该在后台执行。
  • 实现 jobs 指令列出当前 shell 中所有后台任务的状态。你需要为任务创建 ID(每次启动 mosh 时,任务从 1 开始编号,每个新增任务编号应加 1),并且通过 jobs 指令输出包括:任务 ID(job_id)、任务的运行状态(status:可能的取值为 RunningDone)、任务的进程 ID(env_id)与运行任务时输入的指令(cmd)。请以 printf("[%d] %-10s 0x%08x %s", job_id, status, env_id, cmd) 的格式进行输出。
  • 实现 fg 将后台任务带回前台继续运行,用户通过 fg <job_id> 的方式将对应任务带回前台。
  • 实现 kill 指令,用户通过 kill <job_id> 来实现结束后台任务。

fgkill 指令中,若 job_id 对应的后台任务不存在则输出 printf("fg: job (%d) do not exist\n", job_id),若 job_id 对应的 ID 为 envid 的进程状态不为 Running 则输出 printf("fg: (0x%08x) not running\n", envid)

例如:
sleep 10&
sleep 60 &
jobs
# wait for about 10 seconds...
jobs

依次执行上述指令,则第一个 jobs 应输出(其中进程 ID 的值可能与你本地运行的输出结果不同):
[1] Running 0x00003805 sleep 10&
[2] Running 0x00005006 sleep 60 &
第二个 jobs 应输出:
[1] Done 0x00003805 sleep 10&
[2] Running 0x00005006 sleep 60 &

  • 通过parsecmd中增加case &,来修改background
    • background值直接不ipc_recv(0,0,0),当前sh.b直接exit()
      1
      2
      3
      4
      if (bg == 1) {
      syscall_add_history(news);
      exit();
      }
  • 对于后台程序
    • 开一个子进程托管
    • 先关信息记录在内核中
      1
      2
      3
      4
      5
      6
      7
      8
      9
      // parsecmd()中
      case '&':
      *bg = 1;
      if (fork() == 0) {
      *bg = 2;
      return argc;
      } else {
      return argc;
      }
      1
      2
      3
      4
      5
      6
      // runcmd()中
      child = spawn(argv[0], argv);
      // debugf("创建echo子进程:%x\n",child);
      if (bg == 2) {
      syscall_add_jobs(child,news);
      }
  • 然后fgkill指令就调用内核指令去找到这个进程
    • fg的话就让当前sh.b等待,直到子进程运行完毕
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      else if (isfg(s) == 0) {
      int job_id = atoi(s+3);
      int envid = syscall_find_envid(job_id);
      if (envid != -1) {
      wait(envid);
      }

      syscall_add_history(news);
      exit();
      }
    • kill的话直接杀死进程,结束sh.b
      1
      2
      3
      4
      5
      6
      7
      else if (iskill(s) == 0) {
      int job_id = atoi(s+5);
      syscall_kill_job(job_id);

      syscall_add_history(news);
      exit();
      }
  • Title: Chap.3 OS-挑战性任务shell
  • Author: Toryn
  • Created at : 2024-06-28 09:59:54
  • Updated at : 2024-06-28 10:46:31
  • Link: https://linboyan-trc.github.io/2024/06/28/Chap-3-OS-challenge-shell/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments