Это старая задача реализации двойного косвенного 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
, чтобы он был достаточно большим для двойных косвенных указателей.