簡介
bitmap.c 位於 fs/bitmap.c路徑下,其包含函式的主要作用是用於維護inode和block的資料結構。
程式碼瀏覽
1)bitmap.c
clear_bit / set_bit
該部分的彙編函式是通過彙編指令test and set指令進行併發控制
/**
** 備註:
** btsl指令: 將基地址%3 和 偏移量 %2 所對應的位存放到進位標識位CF上,然後設定該位為1.
** setb指令: 將CF對應的值複製到對應暫存器上。
** 思考:
** 通過該兩個指令可以實現一個同步的機制,比如while(set_bit(nr, addr))或if(!set_bit(nr, addr));
** 該過程保證某一時刻只有一個程式可以通過該判斷,其他程式可以繼續等待或者跳過。
**
**/
#define set_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
#define clear_bit(nr,addr) ({\
register int res __asm__("ax"); \
__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
複製程式碼
find_first_zero
該函式非常常用,因為linux檔案系統包含了inode點陣圖和block點陣圖,通過點陣圖的index去找到對應的點陣圖結構體,通過點陣圖結構體找到相應的塊內容。
可以看出以下程式碼,通過bsfl對eax暫存器進行對比獲取第一位值,如果找不到,則跳轉ecx並繼續嘗試。
#define find_first_zero(addr) ({ \
int __res; \
__asm__("cld\n" \ // 清方向位
"1:\tlodsl\n\t" \ // 取[esi] --> eax
"notl %%eax\n\t" \ // 將eax中每位都取反
"bsfl %%eax,%%edx\n\t" \ // 從0位開始掃描掃描eax中是1的第一位,其偏移量 --> edx
"je 2f\n\t" \ // 加入eax為0,則跳轉到2的位置
"addl %%edx,%%ecx\n\t" \ // 偏移量加上ecx (ecx為第一個0值位的偏移值)
"jmp 3f\n" \ // 跳轉到3位置
"2:\taddl $32,%%ecx\n\t" \ // ecx+1個長位元組大小
"cmpl $8192,%%ecx\n\t" \ // 是否已經掃描了8192位了
"jl 1b\n" \ // 若沒有掃描完則跳回1位置
"3:" \
:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})
複製程式碼
block塊的操作 (free/new)
block bitmap大小為8個8192位大小的陣列。
void free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
// first find super_block
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");
// find dev^block ==> bh
bh = get_hash_table(dev,block);
if (bh) {
if (bh->b_count != 1) {
printk("trying to free block (%04x:%d), count=%d\n",
dev,block,bh->b_count);
return;
}
bh->b_dirt=0;
bh->b_uptodate=0;
// clear bh content and put it dirty
brelse(bh);
}
block -= sb->s_firstdatazone - 1 ;
// clear bitmap table and set block unused.
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");
}
// 設定block bitmap塊為dirty
sb->s_zmap[block/8192]->b_dirt = 1;
}
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;
// 遍歷8次的8192位資訊,找到合適的一個位
for (i=0 ; i<8 ; 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;
// 設定block使用位
if (set_bit(j,bh->b_data))
panic("new_block: bit already set");
bh->b_dirt = 1;
j += i*8192 + sb->s_firstdatazone-1;
if (j >= sb->s_nzones)
return 0;
// 找到j對應的block塊,清空block內容,不為1則停機
if (!(bh=getblk(dev,j)))
panic("new_block: cannot get block");
if (bh->b_count != 1)
panic("new block: count is != 1");
clear_block(bh->b_data);
bh->b_uptodate = 1;
bh->b_dirt = 1;
brelse(bh);
return j;
}
複製程式碼
inode塊操作 (free/new)
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\n",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 對應的i_num為inode點陣圖對應位置的索引號,進行右移獲取對應的map索引
*/
if (!(bh=sb->s_imap[inode->i_num>>13]))
panic("nonexistent imap in superblock");
if (clear_bit(inode->i_num&8191,bh->b_data))
panic("free_inode: bit already cleared");
bh->b_dirt = 1;
memset(inode,0,sizeof(*inode));
}
struct m_inode * new_inode(int dev)
{
struct m_inode * inode;
struct super_block * sb;
struct buffer_head * bh;
int i,j;
// 建立一個空白的inode結構
if (!(inode=get_empty_inode()))
return NULL;
if (!(sb = get_super(dev)))
panic("new_inode with unknown device");
// 查詢一個空閒的inode點陣圖位置
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;
}
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_dirt=1;
// 為inode設定i_num 以及相關屬性
inode->i_num = j + i*8192;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
return inode;
}
複製程式碼
- truncate.c
truncate.c 包含三個函式,該c檔案的作用是清空inode指向的i_zone空間,清除分為三部,一步是清除直接block,一步是清除一級間接block,一步是清除二級間接block
void truncate(struct m_inode * inode)
{
int i;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return;
//遍歷前6個直接block,呼叫free_block清除點陣圖上對應的位數
for (i=0;i<7;i++)
if (inode->i_zone[i]) {
free_block(inode->i_dev,inode->i_zone[i]);
inode->i_zone[i]=0;
}
// 清除間接block
free_ind(inode->i_dev,inode->i_zone[7]);
free_dind(inode->i_dev,inode->i_zone[8]);
inode->i_zone[7] = inode->i_zone[8] = 0;
inode->i_size = 0;
inode->i_dirt = 1;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
static void free_ind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
if (!block)
return;
// 讀取間接塊指向的內容到buffer_head上,假設快取記憶體中不存在該快取,則其會呼叫驅動讀取檔案並複製到對應的b_data上。
if (bh=bread(dev,block)) {
// 獲取buffer_head的b_data,並根據16位大小進行讀取block號
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
free_block(dev,*p);
brelse(bh);
}
free_block(dev,block);
}
static void free_dind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
if (!block)
return;
if (bh=bread(dev,block)) {
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
free_ind(dev,*p);
brelse(bh);
}
free_block(dev,block);
}
複製程式碼