linux 0.12 核心學習 (bitmap.c/ truncate.c)

渣渣強發表於2018-12-30

簡介

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;
}
複製程式碼
  1. 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);
}

複製程式碼

相關文章