Xv6

Lab: Xv6 and Unix utilities

utilities

pingpong

pingpong (easy) Write a program that uses UNIX 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.

Some hints:

  • Use pipe to create a pipe.
  • Use fork to create a child.
  • Use read to read from the pipe, and write to write to the pipe.
  • Use getpid to find the process ID of the calling process.
  • Add the program to UPROGS in Makefile.
  • User programs on xv6 have a limited set of library functions available to them. You can see the list in user/user.h; the source (other than for system calls) is in user/ulib.c, user/printf.c, and user/umalloc.c. Run the program from the xv6 shell and it should produce the following output:
    $ make qemu
    ...
    init: starting sh
    $ pingpong
    4: received ping
    3: received pong
    $

Your solution is correct if your program exchanges a byte between two processes and produces output as shown above. pingpong诚然是比较简单的一个程序,但是需要了解的system function如fork和pipe却都并不简单.

fork

使用chatgpt可以大概了解fork的 阅读xv6-book 也可以大概了解fork.

dirent stat
#define DIRSIZ 14

struct dirent {
  ushort inum;
  char name[DIRSIZ];
  char isvalid;
};
``` c
struct stat {
  short type;  // 文件类型,如 T_DIR, T_FILE 等
  int dev;     // 磁盘设备号
  uint ino;    // inode 号
  short nlink; // 硬链接数
  uint size;   // 文件大小(字节)
  uint mtime;  // 最近的修改时间
};

暂时就了解这么多吧,真正底层的东西在后面文件系统中会详细学到.

int fd;
  // dirent 即目录项,
  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);
    break;

ls和find会利用到stat结构体中的type来判断文件类型

buf缓冲区

使用buf缓冲区来作为绝对路径,使用char *p来作为文件名,通过加’/‘前缀拼接在buf后面 p = buf + strlen(buf);

void find(char *path,char *target){
    int fd;
    struct dirent dt;
    struct stat st;
    char buf[512],*p;
    if ((fd = open(path,0)) < 0)
    {
      printf("cann't not open %s\n",path);
      return;
    }
    if(fstat(fd,&st)<0){
      printf("can't stat %s\n",path);
      close(fd);
      return;
    }
    if(st.type!=T_DIR){
      fprintf(2,"Usage: find: %s is not a directory",path);
      close(fd);
      return;
    }
    strcpy(buf,path);
    //buf是绝对路径,p是一个文件名,通过加'/'前缀拼接在buf后面
    p=buf+strlen(buf);
    *p++='/';
    //读取fd,如果read返回字节数和de长度相等则循环
    while(read(fd,&dt,sizeof(dt))==sizeof(dt)){
      if(dt.inum==0){
        continue;
      }
      if(!(strcmp(dt.name,"."))||!(strcmp(dt.name,".."))){
        continue;
      }
      memmove(p,dt.name,DIRSIZ);
      //设置文件名结束符
      p[DIRSIZ]=0;
      //int stat(char*,DIRSIZ);
      //stat 系统调用,可以获得一个已存在文件的模式,并将此模式赋值给它的副本
      if(stat(buf,&st)<0){
        fprintf(2,"find: can't stat %s\n",buf);
        continue;
      }
      if(st.type==T_DIR){
        find(buf,target);
      }
      else if(st.type==T_FILE&&!strcmp(dt.name,target)){
        printf("%s\n",buf);
      }
    }   
}

system call

在你开始写代码之前,请阅读xv6手册《book-riscv-rev1》的第2章、第4章的第4.3节和第4.4节以及相关源代码文件:

  • 系统调用的用户空间代码在user/user.h和user/usys.pl中
  • 内核空间代码是kernel/syscall.h、kernel/syscall.c
  • 与进程相关的代码是kernel/proc.h和kernel/proc.c