/* This file manages the super block table and the related data structures, * namely, the bit maps that keep track of which zones and which inodes are * allocated and which are free. When a new inode or zone is needed, the * appropriate bit map is searched for a free entry. * * The entry points into this file are * alloc_bit: somebody wants to allocate a zone or inode; find one * free_bit: indicate that a zone or inode is available for allocation * get_super: search the 'superblock' table for a device * mounted: tells if file inode is on mounted (or ROOT) file system * read_super: read a superblock */ #include "fs.h" #include <string.h> #include <minix/boot.h> #include "buf.h" #include "inode.h" #include "super.h" #define BITCHUNK_BITS (usizeof(bitchunk_t) * CHAR_BIT) #define BITS_PER_BLOCK (BITMAP_CHUNKS * BITCHUNK_BITS)
说明:位示图存储块中把所有的位在逻辑上分成一个个的bit_chunk,每个bit_chunk中包含的位数为宏定义BITCHUNK_BITS .一个物理块中存储BITMAP_CHUNKS个bit_chunk.在块缓冲中的定义为:
EXTERN struct buf { /* Data portion of the buffer. */ union { char b__data[BLOCK_SIZE]; /* ordinary user data */ struct direct b__dir[NR_DIR_ENTRIES]; /* directory block */ zone1_t b__v1_ind[V1_INDIRECTS]; /* V1 indirect block */ zone_t b__v2_ind[V2_INDIRECTS]; /* V2 indirect block */ d1_inode b__v1_ino[V1_INODES_PER_BLOCK]; /* V1 inode block */ d2_inode b__v2_ino[V2_INODES_PER_BLOCK]; /* V2 inode block */ bitchunk_t b__bitmap[BITMAP_CHUNKS]; /* bit map block */ } b; /* Header portion of the buffer. */ struct buf *b_next; /* used to link all free bufs in a chain */ struct buf *b_prev; /* used to link all free bufs the other way */ struct buf *b_hash; /* used to link bufs on hash chains */ block_t b_blocknr; /* block number of its (minor) device */ dev_t b_dev; /* major | minor device where block resides */ char b_dirt; /* CLEAN or DIRTY */ char b_count; /* number of users of this buffer */ } buf[NR_BUFS]; /* A block is free if b_dev == NO_DEV. */ #define NIL_BUF ((struct buf *) 0) /* indicates absence of a buffer */ /* These defs make it possible to use to bp->b_data instead of bp->b.b__data */ #define b_data b.b__data #define b_dir b.b__dir #define b_v1_ind b.b__v1_ind #define b_v2_ind b.b__v2_ind #define b_v1_ino b.b__v1_ino #define b_v2_ino b.b__v2_ino #define b_bitmap b.b__bitmap EXTERN struct buf *buf_hash[NR_BUF_HASH]; /* the buffer hash table */ EXTERN struct buf *front; /* points to least recently used free block */ EXTERN struct buf *rear; /* points to most recently used free block */ EXTERN int bufs_in_use; /* # bufs currently in use (not on free list)*/ /* When a block is released, the type of usage is passed to put_block(). */ #define WRITE_IMMED 0100 /* block should be written to disk now */ #define ONE_SHOT 0200 /* set if block not likely to be needed soon */ #define INODE_BLOCK (0 + MAYBE_WRITE_IMMED) /* inode block */ #define DIRECTORY_BLOCK (1 + MAYBE_WRITE_IMMED) /* directory block */ #define INDIRECT_BLOCK (2 + MAYBE_WRITE_IMMED) /* pointer block */ #define MAP_BLOCK (3 + MAYBE_WRITE_IMMED) /* bit map */ #define ZUPER_BLOCK (4 + WRITE_IMMED + ONE_SHOT) /* super block */ #define FULL_DATA_BLOCK 5 /* data, fully used */ #define PARTIAL_DATA_BLOCK 6 /* data, partly used*/ #define HASH_MASK (NR_BUF_HASH - 1) /* mask for hashing block numbers */
bitchunk_t b__bitmap[BITMAP_CHUNKS]; /* bit map block */
/*===========================================================================* * alloc_bit * *===========================================================================*/ PUBLIC bit_t alloc_bit(sp, map, origin) struct super_block *sp; /* the filesystem to allocate from */ int map; /* IMAP (inode map) or ZMAP (zone map) */ bit_t origin; /* number of bit to start searching at */ { /* Allocate a bit from a bit map and return its bit number. */ block_t start_block; /* first bit block */ bit_t map_bits; /* how many bits are there in the bit map? */ unsigned bit_blocks; /* how many blocks are there in the bit map? */ unsigned block, word, bcount, wcount; struct buf *bp; bitchunk_t *wptr, *wlim, k; bit_t i, b; if (sp->s_rd_only) panic("can't allocate bit on read-only filesys.", NO_NUM); if (map == IMAP) { start_block = SUPER_BLOCK + 1; map_bits = sp->s_ninodes + 1; bit_blocks = sp->s_imap_blocks; } else { start_block = SUPER_BLOCK + 1 + sp->s_imap_blocks; map_bits = sp->s_zones - (sp->s_firstdatazone - 1); bit_blocks = sp->s_zmap_blocks; } /* Figure out where to start the bit search (depends on 'origin'). */ if (origin >= map_bits) origin = 0; /* for robustness */ /* Locate the starting place. */ block = origin / BITS_PER_BLOCK; word = (origin % BITS_PER_BLOCK) / BITCHUNK_BITS; /* Iterate over all blocks plus one, because we start in the middle. */ bcount = bit_blocks + 1; do { bp = get_block(sp->s_dev, start_block + block, NORMAL); wlim = &bp->b_bitmap[BITMAP_CHUNKS]; /* Iterate over the words in block. */ for (wptr = &bp->b_bitmap[word]; wptr < wlim; wptr++) { /* Does this word contain a free bit? */ if (*wptr == (bitchunk_t) ~0) continue; /* Find and allocate the free bit. */ k = conv2(sp->s_native, (int) *wptr); for (i = 0; (k & (1 << i)) != 0; ++i) {} /* Bit number from the start of the bit map. */ b = ((bit_t) block * BITS_PER_BLOCK) + (wptr - &bp->b_bitmap[0]) * BITCHUNK_BITS + i; /* Don't allocate bits beyond the end of the map. */ if (b >= map_bits) break; /* Allocate and return bit number. */ k |= 1 << i; *wptr = conv2(sp->s_native, (int) k); bp->b_dirt = DIRTY; put_block(bp, MAP_BLOCK); return(b); } put_block(bp, MAP_BLOCK); if (++block >= bit_blocks) block = 0; /* last block, wrap around */ word = 0; } while (--bcount > 0); return(NO_BIT); /* no bit could be allocated */ }
注意分配得到的位示图号并不一定等于origin.
origin只是指示了一个分配开始的地点.
此函数在分配块的alloc_zone和分配inode的alloc_inode中都有调用.因此alloc_bit要处理的是两个位示图.
(1)根据参数不同设置不同的位示图块起始块号start_block
(2)设置位示图中的总位数map_bits:在inode位示图中,sp->s_ninodes表示设备上可用的inode总数目,但这个并不是等于位示图中的总位数,因为有0号inode为保留,仅仅用作指示错误。但是在位示图中还是为0号inode保留的一位标记。因此inode bit_map中总位数为sp->s_ninodes + 1
对于数据块位示图,总的数据块数目由sp->s_zones指示。这个值同样不等于数据块位示图的总位数。因为磁盘上有部分blk是不参与分配的,因此在位示图中不予指示。这些块有引导块、超级块、inode位示图块、zone位示图块以及inode存储块等。因此要从总的块数中减去这些不参与分配的块才是位示图中的总位数。这里使用超级块中的sp->s_firstdatazone指示磁盘上可分配的第一个数据块号。之前都是系统保留的块。因此可以使用sp->s_zones - sp->s_firstdatazone + 1表示所有可分配的块数。
(3)设置bit_blocks:这个直接在超级块中有记录,即sp->s_imap_blocks (imap占用的数据块数)
sp->s_imap_blocks (zmap占用的数据块数)
下面计算origin对应在bit_map_blk中的位置。从上面知道了位示图块起始块号。根据origin计算出相对于起始块的块号,即:
/* Locate the starting place. */ block = origin / BITS_PER_BLOCK; word = (origin % BITS_PER_BLOCK) / BITCHUNK_BITS;
然后从块号为start_blk + block 的块中的第word个bit_chunk开始查找一个空闲的bit。
设置遍历块的数目为b_count = bit_blocks + 1的原因是,我们遍历开始的地方是在一个块的中间(第word个bit_chunk)。如果仅遍历bit_blocks个块,采用wrap_around的方式,只能遍历到开始bit_chunk所在块之前的那个blk,而在此bit_chunk所在块内,前面0~word - 1号bit_chunk并没有遍历到。
查找空闲bit是在bit_chunk中进行.先检查bit_chunk中的bit是否以被全部占用。没有的话可以确定这个bit_chunk中有空闲的位。继续确定是bit_chunk中的第i位。
最终分配的bit为:
/* Bit number from the start of the bit map. */ b = ((bit_t) block * BITS_PER_BLOCK) + (wptr - &bp->b_bitmap[0]) * BITCHUNK_BITS + i;
k |= 1 << i; *wptr = conv2(sp->s_native, (int) k);
最好put_block,释放bitmap_blk占用的缓冲区块。
从上面的分配bit的过程可以看到,给出了origin只是指定了分配bit的一个大致位置,具体到位示图块中的bit_chunk。
/*===========================================================================* * free_bit * *===========================================================================*/ PUBLIC void free_bit(sp, map, bit_returned) struct super_block *sp; /* the filesystem to operate on */ int map; /* IMAP (inode map) or ZMAP (zone map) */ bit_t bit_returned; /* number of bit to insert into the map */ { /* Return a zone or inode by turning off its bitmap bit. */ unsigned block, word, bit; struct buf *bp; bitchunk_t k, mask; block_t start_block; if (sp->s_rd_only) panic("can't free bit on read-only filesys.", NO_NUM); if (map == IMAP) { start_block = SUPER_BLOCK + 1; } else { start_block = SUPER_BLOCK + 1 + sp->s_imap_blocks; } block = bit_returned / BITS_PER_BLOCK; word = (bit_returned % BITS_PER_BLOCK) / BITCHUNK_BITS; bit = bit_returned % BITCHUNK_BITS; mask = 1 << bit; bp = get_block(sp->s_dev, start_block + block, NORMAL); k = conv2(sp->s_native, (int) bp->b_bitmap[word]); if (!(k & mask)) { panic(map == IMAP ? "tried to free unused inode" : "tried to free unused block", NO_NUM); } k &= ~mask; bp->b_bitmap[word] = conv2(sp->s_native, (int) k); bp->b_dirt = DIRTY; put_block(bp, MAP_BLOCK); }
/*===========================================================================* * read_super * *===========================================================================*/ PUBLIC int read_super(sp) register struct super_block *sp; /* pointer to a superblock */ { /* Read a superblock. */ register struct buf *bp; dev_t dev; int magic; int version, native; dev = sp->s_dev; /* save device (will be overwritten by copy) */ bp = get_block(sp->s_dev, SUPER_BLOCK, NORMAL); memcpy( (char *) sp, bp->b_data, (size_t) SUPER_SIZE); put_block(bp, ZUPER_BLOCK); sp->s_dev = NO_DEV; /* restore later */ magic = sp->s_magic; /* determines file system type */ /* Get file system version and type. */ if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) { version = V1; native = (magic == SUPER_MAGIC); } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) { version = V2; native = (magic == SUPER_V2); } else { return(EINVAL); } /* If the super block has the wrong byte order, swap the fields; the magic * number doesn't need conversion. */ sp->s_ninodes = conv2(native, (int) sp->s_ninodes); sp->s_nzones = conv2(native, (int) sp->s_nzones); sp->s_imap_blocks = conv2(native, (int) sp->s_imap_blocks); sp->s_zmap_blocks = conv2(native, (int) sp->s_zmap_blocks); sp->s_firstdatazone = conv2(native, (int) sp->s_firstdatazone); sp->s_log_zone_size = conv2(native, (int) sp->s_log_zone_size); sp->s_max_size = conv4(native, sp->s_max_size); sp->s_zones = conv4(native, sp->s_zones); /* In V1, the device size was kept in a short, s_nzones, which limited * devices to 32K zones. For V2, it was decided to keep the size as a * long. However, just changing s_nzones to a long would not work, since * then the position of s_magic in the super block would not be the same * in V1 and V2 file systems, and there would be no way to tell whether * a newly mounted file system was V1 or V2. The solution was to introduce * a new variable, s_zones, and copy the size there. * * Calculate some other numbers that depend on the version here too, to * hide some of the differences. */ if (version == V1) { sp->s_zones = sp->s_nzones; /* only V1 needs this copy */ sp->s_inodes_per_block = V1_INODES_PER_BLOCK; sp->s_ndzones = V1_NR_DZONES; sp->s_nindirs = V1_INDIRECTS; } else { sp->s_inodes_per_block = V2_INODES_PER_BLOCK; sp->s_ndzones = V2_NR_DZONES; sp->s_nindirs = V2_INDIRECTS; } sp->s_isearch = 0; /* inode searches initially start at 0 */ sp->s_zsearch = 0; /* zone searches initially start at 0 */ sp->s_version = version; sp->s_native = native; /* Make a few basic checks to see if super block looks reasonable. */ if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1 || sp->s_ninodes < 1 || sp->s_zones < 1 || (unsigned) sp->s_log_zone_size > 4) { return(EINVAL); } sp->s_dev = dev; /* restore device number */ return(OK); }
读取超级块:给定一个超级块内存存储区域指针sp,里面有要读取的超级块所属的设备号。根据此设备号调用get_block将对应超级块读入内存块缓存中。然后将所有信息拷贝到sp指向的内存区域。
根据magic number确定文件系统版本和native值。native为1时不进行byte swap 等于0时进行byte swap。
根据native值重新为sp指向的超级块中各个域赋值。因为sp中当前还是从磁盘读出的crude值,要变成cpu要处理的格式的值,因此要使用conv函数对各个域进行修整。
对于sp->s_nzones和sp->s_zones都代表设备所拥有的zone的总数。二者的差别是一个是short 类型,一个是long类型。前者仅用于版本V1.对于版本1,
sp->s_zones = sp->s_nzones; /* only V1 needs this copy */
因此在处理超级块时可以不用管版本,直接使用sp->s_zones。
然后isearch和zsearch都初始化为0。
疑问:到底什么时候你调用read_super函数?
附上超级块结构定义:
/* Super block table. The root file system and every mounted file system * has an entry here. The entry holds information about the sizes of the bit * maps and inodes. The s_ninodes field gives the number of inodes available * for files and directories, including the root directory. Inode 0 is * on the disk, but not used. Thus s_ninodes = 4 means that 5 bits will be * used in the bit map, bit 0, which is always 1 and not used, and bits 1-4 * for files and directories. The disk layout is: * * Item # blocks * boot block 1 * super block 1 * inode map s_imap_blocks * zone map s_zmap_blocks * inodes (s_ninodes + 'inodes per block' - 1)/'inodes per block' * unused whatever is needed to fill out the current zone * data zones (s_zones - s_firstdatazone) << s_log_zone_size * * A super_block slot is free if s_dev == NO_DEV. */ EXTERN struct super_block { ino_t s_ninodes; /* # usable inodes on the minor device */ zone1_t s_nzones; /* total device size, including bit maps etc */ short s_imap_blocks; /* # of blocks used by inode bit map */ short s_zmap_blocks; /* # of blocks used by zone bit map */ zone1_t s_firstdatazone; /* number of first data zone */ short s_log_zone_size; /* log2 of blocks/zone */ off_t s_max_size; /* maximum file size on this device */ short s_magic; /* magic number to recognize super-blocks */ short s_pad; /* try to avoid compiler-dependent padding */ zone_t s_zones; /* number of zones (replaces s_nzones in V2) */ /* The following items are only used when the super_block is in memory. */ struct inode *s_isup; /* inode for root dir of mounted file sys */ struct inode *s_imount; /* inode mounted on */ unsigned s_inodes_per_block; /* precalculated from magic number */ dev_t s_dev; /* whose super block is this? */ int s_rd_only; /* set to 1 iff file sys mounted read only */ int s_native; /* set to 1 iff not byte swapped file system */ int s_version; /* file system version, zero means bad magic */ int s_ndzones; /* # direct zones in an inode */ int s_nindirs; /* # indirect zones per indirect block */ bit_t s_isearch; /* inodes below this bit number are in use */ bit_t s_zsearch; /* all zones below this bit number are in use*/ } super_block[NR_SUPERS]; #define NIL_SUPER (struct super_block *) 0 #define IMAP 0 /* operating on the inode bit map */ #define ZMAP 1 /* operating on the zone bit map */