/* 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 */
}
alloc_bit函數根據給定的位示圖號分配一個可用的位示圖位,分配失敗的時候返回NO_BIT.
注意分配得到的位示圖號並不一定等於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;
位示圖以數組的形式存放,數組單元爲BITCHUNK。word指示origin在數組中的索引。
然後從塊號爲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;
同時,還要把位示圖中的對應位置爲1.這個由下面語句完成:
k |= 1 << i;
*wptr = conv2(sp->s_native, (int) k);
注:conv2用於轉換字節序列。
最好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);
}
使用掩碼mask將讀出的bitmap_blk修改之後設置髒標誌,然後釋放申請的塊。
/*===========================================================================*
* 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 */