xv6 system pani c при попытке прочитать большой файл - PullRequest
1 голос
/ 30 января 2020

Это старая задача реализации двойного косвенного inode s для системы xv6. При двойном косвенном обращении файл может иметь 16523 блока (11 прямых указателей + 1 одиночный косвенный указатель * 128 + 1 двойной косвенный указатель * 128 * 128), и моя реализация удовлетворяет этому условию в соответствии с выводом usertests ( Я представлю это ниже).

Полный код системы xv6 можно найти по адресу https://github.com/mit-pdos/xv6-public, и, поскольку, если вы здесь, вы, вероятно, знакомы с ним, я представлю только изменения, которые я внес в реализацию методов bmap() и itrunc() в fs.c:

// NINDIRECT is 128
// NDIRECT is 11
// There is one less direct pointer to make room for the double-indirect

// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  uint *indirect;
  struct buf *bp;
  struct buf *dbp;

  // Direct Pointer
  if(bn < NDIRECT){
    // get direct pointer, allocate if necessary
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;

  // Indirect Pointer
  if(bn < NINDIRECT){
    // load indirect block, allocate if necessary
    if((addr = ip->addrs[NDIRECT]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    // get direct pointer, allocate if necessary
    if((addr = a[bn]) == 0){
      a[bn] = addr = balloc(ip->dev);
      log_write(bp);
    }
    // release indirect block
    brelse(bp);
    // return disk block address
    return addr;
  }
  bn -= NINDIRECT;

  // Double-indirect Pointer
  if(bn < NDINDIRECT){
    // load double-indirect block, allocate if necessary
    if((addr = ip->addrs[NDIRECT+1]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    dbp = bread(ip->dev,addr);
    indirect = (uint*)dbp->data;
    // load indirect block, allocate if necessary
    if((addr = indirect[bn/NINDIRECT]) == 0)
      indirect[bn/NINDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev,addr);
    a = (uint*)bp->data;
    // get direct pointer, allocate if necessary
    if((addr = a[bn%NINDIRECT]) == 0){
      a[bn%NINDIRECT] = addr = balloc(ip->dev);
      log_write(bp);
    }
    // release indirect & double-indirect blocks
    brelse(bp);
    brelse(dbp);
    // return disk block address
    return addr;
  }

  panic("bmap: out of range");
}

// Truncate inode (discard contents).
// Only called when the inode has no links
// to it (no directory entries referring to it)
// and has no in-memory reference to it (is
// not an open file or current directory).
static void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp;
  struct buf *dbp;
  uint *a;
  uint *d;

  // free the direct pointers
  for(i = 0; i < NDIRECT; i++){
    if(ip->addrs[i]){
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }

  // free the indirect pointer
  if(ip->addrs[NDIRECT]){
    // load the indirect block
    bp = bread(ip->dev, ip->addrs[NDIRECT]);
    a = (uint*)bp->data;
    // iterate over the block, and free all direct pointers
    for(j = 0; j < NINDIRECT; j++){
      if(a[j])
        bfree(ip->dev, a[j]);
    }
    // free the indirect pointer
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT]);
    ip->addrs[NDIRECT] = 0;
  }

  // free the double indirect pointer
  if(ip->addrs[NDIRECT+1]){
    // load the double-indirect block
    dbp = bread(ip->dev, ip->addrs[NDIRECT+1]);
    d = (uint*)dbp->data;
    // iterate over the block, and free all indirect blocks
    for(k = 0; k < NINDIRECT; k++){
      // load the indirect block
      bp = bread(ip->dev, d[k]);
      a = (uint*)bp->data;
      // iterate over the block, and free all direct pointers
      for(j = 0; j < NINDIRECT; j++){
        if(a[j])
          bfree(ip->dev,a[j]);
      }
      // free the infirect pointer
      brelse(bp);
      bfree(ip->dev, d[k]);
      d[k] = 0;
    }
    // free the double-indirect pointer
    brelse(dbp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }

  ip->size = 0;
  iupdate(ip);
}

Вывод, полученный из утилиты usertests, показывает, что часть записи успешна, но system pani c при попытке прочитать большой файл:

wrote 16523 sectors
closed the file for writing
opend the file for reading
read 0 sectors
lapicid 1: panic: log_write outside of trans
 80102f18 80101249 80101442 80101b67 80100f89 80104b42 8010482a 80105829 80105572 0

Я немного изменил тест bigfile(), поэтому я также опубликую его здесь:

void
bigfile(void)
{
  char buf[512];
  int fd, i, sectors;
  unlink("big.file");
  fd = open("big.file", O_CREATE | O_WRONLY);
  if(fd < 0){
    printf(2, "big: cannot open big.file for writing\n");
    exit();
  }

  sectors = 0;
  while(1){
    *(int*)buf = sectors;
    int cc = write(fd, buf, sizeof(buf));
    if(cc <= 0)
      break;
    sectors++;
        if (sectors % 100 == 0){
                printf(2, ".");
                printf(2,"sector: %d\n",sectors);
        }
  }

  printf(1, "\nwrote %d sectors\n", sectors);

  close(fd);
  printf(1,"closed the file for writing\n");

  fd = open("big.file", O_RDONLY);
  if(fd < 0){
    printf(2, "big: cannot re-open big.file for reading\n");
    exit();
  }
  printf(1,"opend the file for reading\n");

  for(i = 0; i < sectors; i++){
    int cc = read(fd, buf, sizeof(buf));
    if(cc <= 0){
      printf(2, "big: read error at sector %d\n", i);
      exit();
    }
    if(*(int*)buf != i){
      printf(2, "big: read the wrong data (%d) for sector %d\n",
             *(int*)buf, i);
      exit();
    }
    if(i%100 == 0)
        printf(2,"read %d sectors\n",i);
  }

  close(fd);
  unlink("big.file");

  printf(1, "bigfile test ok\n");
}

Заранее благодарю за любую помощь. PS: конечно, я изменил размер FSSIZE в файле param.h, чтобы он был достаточно большим для двойных косвенных указателей.

...