环境搭建

其实没什么好说的,用 Linux 很熟了,在 Debian 上跟步骤 apt install 就行了 (I’m not using arch btw)
盲猜很多人会卡在 git clone
我用的是 2020 版因为我已经做过了 🤣,再回味了一遍

解题思路

踩的一个坑是头文件有依赖关系,必须安装顺序 include, (linker is awesome btw), 然后 clang-format 自动给我 SortInclude 搞得我莫名其妙会失败,汗
还有就是 VSCode Makefile Extension 会导致 include 有问题,很迷

sleep

属于是练手题,让你知道怎么加 user program
然后知道 argc, argv, 转换 stringint 就行了?

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

pingpong

pipe 练手题,要知道 fork and pipe 的用法,read, write, fd, parent and child
经典 IPC btw

The wait(0) function is a system call that suspends the execution of the calling process until one of its child processes terminates or a signal is received.

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
 
int
main (int argc, char const *argv[])
{
  // index 0 -> in, 1 -> out, like fd 0 1 2
  int p2c[2], c2p[2];
  pipe (p2c); // from parent to child
  pipe (c2p); // from child to parent
 
  if (fork () != 0)
    { // parent
      write (p2c[1], "ping", 4);
      char readin_buf[4];
      read (c2p[0], readin_buf, 4);
      printf ("%d: received pong\n", getpid ());
      wait (0); // wait for child to finish
    }
  else
    {
      char readin_buf[4];
      read (p2c[0], readin_buf, 4);
      printf ("%d: received ping\n", getpid ());
      write (c2p[1], readin_buf, 4);
    }
 
  exit(0);
}
 
$ pingpong
4: received ping
3: received pong

prime

这个还是非常有意思有难度的,和 MapReduce 有点像,又和 functional programming 中的 filter 有点像
中文文档写的不太好,没有很体现 筛 (sieve)

-1 作为 sentinel to indicate the end of input

第一次没做出来,再做了一次
要注意关 fd 不然会莫名其妙爆掉,查网上看到的,汗
有时候会纠结要不要 return, exit ,不过感觉重点是 wait

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
 
void
sieve (int p_left[2])
{
  // the 1st input is guaranteed to be prime since non-primes are filterd out
  int init_prime;
  read (p_left[0], &init_prime, sizeof (init_prime));
  if (init_prime == -1)
    {
      exit (0);
    }
 
  printf ("prime %d\n", init_prime);
 
  int p_right[2];
  pipe (p_right);
 
  if (fork () == 0)
    {
      // child
      close (p_left[0]);
      close (p_right[1]);
      sieve (p_right);
    }
  else
    {
      // parent
      close (p_right[0]);
      int i;
      while (read (p_left[0], &i, sizeof (i)) && i != -1)
        {
          if (i % init_prime != 0)
            {
              write (p_right[1], &i, sizeof (i));
            }
        }
      i = -1;
      write (p_right[1], &i, sizeof (i));
 
      wait (0);
    }
}
 
int
main (int argc, char const *argv[])
{
  int p[2];
  pipe (p);
 
  if (fork () == 0)
    { // child
      close (p[1]);
      sieve (p);
    }
  else
    {               // parent
      close (p[0]); // parent no need to read
      int i;
      for (i = 2; i <= 35; i++)
        {
          write (p[1], &i, sizeof (i));
        }
      i = -1;
      write (p[1], &i, sizeof (i));
      wait (0);
    }
 
  exit (0);
}

find

最无聊的一个,照抄就有
second thought: 写错了,害得后面 xargs test 也错了,💩

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
 
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, "find: cannot open %s\n", path);
      return;
    }
 
  if (fstat (fd, &st) < 0)
    {
      fprintf (2, "find: cannot stat %s\n", path);
      close (fd);
      return;
    }
 
  switch (st.type)
    {
    case T_FILE:
      if (strcmp (path + strlen (path) - strlen (target), target) == 0)
        {
          printf ("%s\n", path);
        }
      break;
    case T_DIR:
      if (strlen (path) + 1 + DIRSIZ + 1 > sizeof buf)
        {
          printf ("find: 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 ("find: cannot stat %s\n", buf);
              continue;
            }
 
          // omit . and ..
          if (strcmp (buf + strlen (buf) - 2, "/.") != 0
              && strcmp (buf + strlen (buf) - 3, "/..") != 0)
            {
              find (buf, target); // recursive find
            }
        }
      break;
    }
  close (fd);
}
 
int
main (int argc, char *argv[])
{
  if (argc < 3)
    {
      exit (0);
    }
  char target[512];
  target[0] = '/';
  strcpy (target + 1, argv[2]);
  find (argv[1], target);
  exit (0);
}

xargs

最烦人的一个,莫名其妙会报错,gdb 也没调出来,看其他人才发现理解错 xargs 了 (好像是写错 find 了?idk)
之前没写出来,尝试又重写了一次
主要难点是字符串处理,和理解 xargs

#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
 
// definitely not very readline but it suit the case
char *
readline (char *buf)
{
  char *pos = buf;
  while (read (0, pos, 1) != 0)
    {
      if (*pos == '\n' || *pos == '\0')
        {
          *pos = '\0';
          return pos;
        }
      pos++;
    }
  return 0;
}
 
void
run (char *program, char *argv[])
{
  if (fork () == 0)
    { // child
      exec (program, argv);
    }
  else
    {
      wait (0);
    }
}
 
int
main (int argc, char *argv[])
{
  char *program = argv[1];
  char *args[MAXARG];
  char readin_buf[2048];
 
  args[0] = program;
  int i = 1;
  // 0 is xargs, 1 is program, first arg starts at 2
  // this is tricky and ugly but it works so whatever
  while (i < argc - 1)
    {
      args[i] = argv[i + 1];
      i++;
    }
 
  char *str_end = readline (readin_buf);
  while (str_end != 0)
    {
      args[i] = readin_buf;
      i++;
      str_end = readline (str_end + 1);
    }
  args[i] = 0;
 
  run (program, args);
  exit (0);
}

实验心得

水平比第一次做的时候提升了,能写出来了😂
总体写的十分开心
或许可以加个 gdb 实验?虽然 printf 也很够了
sieve 的中文文档不太行
gnu c codestyle 是真的丑
好像比较麻木

make grade 似乎没提到?

== Test sleep, no arguments == 
$ make qemu-gdb
sleep, no arguments: OK (3.3s) 
== Test sleep, returns == 
$ make qemu-gdb
sleep, returns: OK (1.4s) 
== Test sleep, makes syscall == 
$ make qemu-gdb
sleep, makes syscall: OK (1.2s) 
== Test pingpong == 
$ make qemu-gdb
pingpong: OK (1.1s) 
== Test primes == 
$ make qemu-gdb
primes: OK (1.2s) 
== Test find, in current directory == 
$ make qemu-gdb
find, in current directory: OK (1.3s) 
== Test find, recursive == 
$ make qemu-gdb
find, recursive: OK (1.8s) 
== Test xargs == 
$ make qemu-gdb
xargs: OK (1.6s) 
== Test time == 
time: OK 
Score: 100/100