MIT-6.S081-2020實驗(xv6-riscv64)九:fs


實驗文檔

概述

這次實驗涉及文件系統,重點是對inode節點的操作。

內容

Large files

這個任務主要目的是支持更大的文件。和內存映射類似,文件系統中也有一個類似“頁表”的結構,每個文件(inode)都有自己的一個“頁表”,維護自己文件占用的文件塊。和內存不同的是,這個“頁表”的級別是自定義的,原始的xv6的表有13項,前12項直接包含文件塊的地址,第13項是二級表的地址,二級表包含256項,每項都是文件塊的地址,所以單個文件最大為12+256個文件塊,為了支持更大的文件,需要將直接包含文件塊地址的表項中取一項支持三級表,這樣單個文件就可以擴大到11+256+256*256塊。整體思路還是很清晰的,bmap函數添加:

  bn -= NINDIRECT;

  if(bn < NININDIRECT){
      int lev1 = bn / NINDIRECT, lev2 = bn % NINDIRECT;
    // Load indirect block, allocating if necessary.
    if((addr = ip->addrs[NDIRECT + 1]) == 0)
      ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[lev1]) == 0){
      a[lev1] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[lev2]) == 0){
      a[lev2] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }
  panic("bmap: out of range");

itrunc函數也做相應的添加:

  if(ip->addrs[NDIRECT + 1]){
    bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
    a = (uint*)bp->data;
    for(j = 0; j < NINDIRECT; j++) {
      if(a[j]) {
        bp2 = bread(ip->dev, a[j]);
        a2 = (uint*)bp2->data;
        for(k = 0; k < NINDIRECT; k++) if (a2[k]) bfree(ip->dev, a2[k]);
        brelse(bp2);
        bfree(ip->dev, a[j]);
      }
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT + 1]);
    ip->addrs[NDIRECT + 1] = 0;

這個任務主要實現軟符號鏈接,個人還是覺得有點難度,需要對inode節點的各類操作比較熟悉。首先是給inode和dinode添加一個字符串屬性用來存儲符號鏈接中的目標路徑,比如dinode:

struct dinode {
  short type;           // File type
  short major;          // Major device number (T_DEVICE only)
  short minor;          // Minor device number (T_DEVICE only)
  short nlink;          // Number of links to inode in file system
  uint size;            // Size of file (bytes)
  uint addrs[NDIRECT+2];   // Data block addresses
  char target[MAXTARGET];
};

主要這個MAXTARGET這個常量是有講究的,因為dinode是一個一個排布在一個硬盤塊里面的,所以硬盤塊的大小必須是dinode結構體大小的倍數,硬盤塊的大小是常量BSIZE的大小,即1024字節,當沒有target屬性時,dinode的大小為2*4+4+4*13=64,剛好被1024整除,同時查看param.h可以發現xv6規定的路徑長度最大可以為128,所以MAXTARGET還得大於128,因此我取MAXTARGET=192,192+64=256,被1024整除。

然后就是sys_symlink:

uint64 sys_symlink(void) {                                                                                       char target[MAXPATH], path[MAXPATH];
    if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
        return -1;
    begin_op();
    struct inode *ip = create(path, T_SYMLINK, 0, 0);
    if (ip == 0) {
        end_op(); return -1;
    }
    memmove(ip->target, target, sizeof(target));
    iupdate(ip); iunlockput(ip);
    end_op(); return 0;
}

首先需要注意獲得傳入系統調用的字符串需要用argstr,不能用argaddr獲得地址后自己復制,因為那個地址是用戶內存的虛擬地址,現在在內核態,頁表已經被換掉了。這里調用了create函數來創建符號文件,因為create函數里沒有對inode的target屬性賦值,所以需要在這里處理。另外就是create函數返回的有效inode是已經經過iget和ilock的了,所以這里create完就直接賦值target屬性並更新到對應的dinode,然后iunlock解鎖再iput釋放inode指針(合起來是iunlockput函數)。

接着是對open函數的修改,主要就是添加一個沿着符號鏈接不斷查找的過程,經過查找后得到的路徑才是需要open的真正路徑:

  if(omode & O_CREATE){
      ......
  } else {
      int cnt = 0;
      for (;;) {
          ip = namei(path);
          if (ip == 0) {
              end_op(); return -1;
          }
          ilock(ip);
          if(ip->type != T_SYMLINK || (omode & O_NOFOLLOW)) break;
          memmove(path, ip->target, MAXPATH);
          iunlockput(ip);
          cnt++;
          if (cnt > 9) {
              end_op(); return -1;
          }
      }
      ......

namei得到的inode是經過iget但沒ilock的,所以取屬性和修改屬性需先ilock。另外就是memmove參數的最后一個參數需要是MAXPATH而不是sizeof(ip->target),因為inode的target屬性我跟隨dinode的target屬性都設成192字節的字符串,大小大於MAXPATH即128,所以如果按sizeof來復制會溢出。


由於我使用的是虛擬機,雖然我已經通過使用無桌面版arch linux+ssh控制來盡可能減少CPU和內存的消耗了,但文件讀寫還是非常難頂。雖然程序單獨測試正確,思路也和網上別人通過的程序思路基本一樣,最后make grade的時候還是超時了,不得已,把測試腳本里的bigfile和usertests測試時限都改成10分鍾才通過(usertests里的writebig好像在這次實驗里格外耗時)。估計使用真機或WSL讀寫真磁盤來完成本實驗情況會好一些。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM