標籤:

文件系統源碼解析之bitmap.c

bitmap.c主要是利用inode點陣圖和數據塊點陣圖管理硬碟中的inode和數據塊的使用。總共四個函數,分別是新建、銷毀inode,新建、銷毀數據塊四個函數。銷毀的時候只是把點陣圖清0,然後把點陣圖的數據回寫硬碟,並沒有清除硬碟上的數據。新建的時候,主要是先把點陣圖置1,然後獲得一個buffer(沒有數據)或者inode(有數據,inode的一些屬性),然後等待寫入硬碟。

/*
* linux/fs/bitmap.c
*
* (C) 1991 Linus Torvalds
*/

/* bitmap.c contains the code that handles the inode and block bitmaps */
#include <string.h>

#include <linux/sched.h>
#include <linux/kernel.h>

#define clear_block(addr)
__asm__("cld
"
"rep
"
"stosl"
::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
// setb把後面的寄存器置1,btsl把addr的第nr位置1
#define set_bit(nr,addr) ({
register int res __asm__("ax");
__asm__ __volatile__("btsl %2,%3
setb %%al":
"=a" (res):"0" (0),"r" (nr),"m" (*(addr)));
res;})

#define clear_bit(nr,addr) ({
register int res __asm__("ax");
__asm__ __volatile__("btrl %2,%3
setnb %%al":
"=a" (res):"0" (0),"r" (nr),"m" (*(addr)));
res;})

#define find_first_zero(addr) ({
int __res;
__asm__("cld
"
"1: lodsl
"
"notl %%eax
"
"bsfl %%eax,%%edx
"
"je 2f
"
"addl %%edx,%%ecx
"
"jmp 3f
"
"2: addl $32,%%ecx
"
"cmpl $8192,%%ecx
"
"jl 1b
"
"3:"
:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si");
__res;})
// 釋放硬碟的某個塊的數據,清除buffer里該數據塊對應的數據,數據塊點陣圖對應的位置0,等待回寫硬碟,硬碟的數據還存在
void free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
// 超級塊里存在文件系統的元數據,包括點陣圖緩存,數據塊的塊數,開始塊號等
if (!(sb = get_super(dev)))
panic("trying to free block on nonexistent device");
// 塊號的範圍,不小於最小,不大於最大
if (block < sb->s_firstdatazone || block >= sb->s_nzones)
panic("trying to free block not in datazone");
// 從緩存中獲取dev設備的第block塊的數據,可能需要釋放
bh = get_hash_table(dev,block);
if (bh) {
// 還在使用
if (bh->b_count != 1) {
printk("trying to free block (%04x:%d), count=%d
",
dev,block,bh->b_count);
return;
}
bh->b_dirt=0;
bh->b_uptodate=0;
// 釋放該buffer給其他進程使用
brelse(bh);
}
block -= sb->s_firstdatazone - 1 ;
/*
置為未使用狀態,s_zmap保存的是硬碟數據塊的點陣圖數組,
每個元素管理8192個數據塊的使用情況,block/8192算出該塊屬於哪個點陣圖元素的管理訪問,
然後把該元素中的地block為清0
*/
if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
panic("free_block: bit already cleared");
}
// 該點陣圖對應的buffer需要回寫到硬碟
sb->s_zmap[block/8192]->b_dirt = 1;
}
/*
新建一個數據塊,首先利用超級塊的塊點陣圖信息找到一個可用的數據塊,
然後讀進來,清0,等待回寫,返回塊號
*/
int new_block(int dev)
{
struct buffer_head * bh;
struct super_block * sb;
int i,j;

if (!(sb = get_super(dev)))
panic("trying to get new block from nonexistant device");
j = 8192;
// 找第一個未使用的塊的位置
for (i=0 ; i<8 ; i++)
//s_zmap[i]為數據塊點陣圖的緩存
if (bh=sb->s_zmap[i])
if ((j=find_first_zero(bh->b_data))<8192)
break;
if (i>=8 || !bh || j>=8192)
return 0;
// 置第j個數據塊已使用標記
if (set_bit(j,bh->b_data))
panic("new_block: bit already set");
// 該點陣圖對應的buffer需要回寫
bh->b_dirt = 1;
// 點陣圖存在多個塊中,i為第i個塊,每個塊對應的點陣圖管理著8192個數據塊
j += i*8192 + sb->s_firstdatazone-1; // 算出塊號
// 超過了最大塊號
if (j >= sb->s_nzones)
return 0;
// 拿到一個buffer,然後清0,等待回寫
if (!(bh=getblk(dev,j)))
panic("new_block: cannot get block");
if (bh->b_count != 1)
panic("new block: count is != 1");
// 清0防止臟數據
clear_block(bh->b_data);
// 內容是最新的
bh->b_uptodate = 1;
// 需要回寫硬碟,因為新建的內容在硬碟還沒有
bh->b_dirt = 1;
brelse(bh);
return j;
}
// 釋放一個inode,把超級塊中的inode點陣圖清0,等待點陣圖回寫到硬碟,但沒有清除硬碟里inode的內容
void free_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;

if (!inode)
return;
if (!inode->i_dev) {
memset(inode,0,sizeof(*inode));
return;
}
if (inode->i_count>1) {
printk("trying to free inode with count=%d
",inode->i_count);
panic("free_inode");
}
if (inode->i_nlinks)
panic("trying to free inode with links");
if (!(sb = get_super(inode->i_dev)))
panic("trying to free inode on nonexistent device");
if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
panic("trying to free inode 0 or nonexistant inode");
// 有多個塊保存這inode點陣圖,每個塊對應8192個,i_num保存的是絕對塊號,除以8192取得該inode在哪個點陣圖塊中
if (!(bh=sb->s_imap[inode->i_num>>13]))
panic("nonexistent imap in superblock");
if (clear_bit(inode->i_num&8191,bh->b_data))
printk("free_inode: bit already cleared.

");
bh->b_dirt = 1;
memset(inode,0,sizeof(*inode));
}

// 新建一個node,首先獲取一個inode的結構,然後把超級塊的inode點陣圖置1
struct m_inode * new_inode(int dev)
{
struct m_inode * inode;
struct super_block * sb;
struct buffer_head * bh;
int i,j;

if (!(inode=get_empty_inode()))
return NULL;
if (!(sb = get_super(dev)))
panic("new_inode with unknown device");
j = 8192;
for (i=0 ; i<8 ; i++)
if (bh=sb->s_imap[i])
if ((j=find_first_zero(bh->b_data))<8192)
break;
if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
iput(inode);
return NULL;
}
// 設置點陣圖的地j位為1
if (set_bit(j,bh->b_data))
panic("new_inode: bit already set");
// 點陣圖回寫硬碟
bh->b_dirt = 1;
inode->i_count=1;
inode->i_nlinks=1;
inode->i_dev=dev;
inode->i_uid=current->euid;
inode->i_gid=current->egid;
// inode的內容也需要回寫硬碟
inode->i_dirt=1;
// 保存的是絕對位置,即位置落在哪個點陣圖塊的第幾個位置,每個點陣圖塊是8192
inode->i_num = j + i*8192;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
return inode;
}

推薦閱讀:

TAG:Linux | Linux內核 |