Linux核心原始碼分析--檔案系統(五、Inode.c)
_bmap()
1、_bmap()函式用於把一個檔案資料塊對映到盤塊的處理操作
因為一個i節點對應一個檔案,所以上面的i節點對映的邏輯塊號就是檔案資料存放的邏輯塊號;i_zone[0]到i_zone[6]是直接邏輯塊號,i_zone[7]是一次間接邏輯塊號,i_zone[8]是二次間接邏輯塊號;檔案中的資料存放在哪個硬碟上的邏輯塊上就是由這個陣列來對映的,根據這個也可以知道一個檔案的最大儲存是多少?
//把檔案上的資料塊對映到磁碟上,inode 檔案i節點;block 檔案中資料塊號,create是否建立標誌 static int _bmap(struct m_inode * inode,int block,int create) { struct buffer_head * bh; int i; //判斷檔案資料塊號block是否超出範圍 if (block<0) panic("_bmap: block<0"); if (block >= 7+512+512*512)//檔案邏輯塊的範圍 panic("_bmap: block>big"); //使用直接塊 if (block<7) { if (create && !inode->i_zone[block])//建立標誌置位,i節點對應的邏輯塊欄位為0 if (inode->i_zone[block]=new_block(inode->i_dev)) {//申請一個新磁碟邏輯塊,返回邏輯號 inode->i_ctime=CURRENT_TIME; inode->i_dirt=1; } return inode->i_zone[block];//返回裝置上的邏輯塊號 } //使用一次間接塊 block -= 7; if (block<512) { if (create && !inode->i_zone[7])//表明檔案是首次使用間接塊,則申請一個磁碟塊來存放間接塊資訊 if (inode->i_zone[7]=new_block(inode->i_dev)) { inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[7])//表示建立間接塊磁碟失敗,或者create未置1 return 0; if (!(bh = bread(inode->i_dev,inode->i_zone[7])))//讀取間接塊的資訊 return 0; i = ((unsigned short *) (bh->b_data))[block];//得到間接塊上block所處的位置,判斷是否為0 if (create && !i)//如果間接塊號上的block位置為0,create置位 if (i=new_block(inode->i_dev)) {//申請一個新的邏輯塊給block ((unsigned short *) (bh->b_data))[block]=i; bh->b_dirt=1; } brelse(bh); return i; } //使用二次間接塊 block -= 512; //為二次間接塊申請邏輯塊 if (create && !inode->i_zone[8]) if (inode->i_zone[8]=new_block(inode->i_dev)) {//對映到二級間接塊中的一級邏輯塊上 inode->i_dirt=1; inode->i_ctime=CURRENT_TIME; } if (!inode->i_zone[8]) return 0; //讀取二級間接塊資訊 if (!(bh=bread(inode->i_dev,inode->i_zone[8]))) return 0; //獲取block二級間接塊號在指定的哪個二級間接塊的一級塊號 i = ((unsigned short *)bh->b_data)[block>>9];//右移9位表示整除512,得到一級塊號 if (create && !i)//如果二級間接塊中二級塊不存在,則要為該二級塊申請一個邏輯塊號 if (i=new_block(inode->i_dev)) {//得到一個邏輯塊用來存放二級間接塊中的二級間接塊號 ((unsigned short *) (bh->b_data))[block>>9]=i;//block>>9 === block/512 為二級塊對映一個邏輯塊 bh->b_dirt=1; } brelse(bh); if (!i) return 0; if (!(bh=bread(inode->i_dev,i)))//讀取二級間接塊中的二級塊對映的快取塊 return 0; i = ((unsigned short *)bh->b_data)[block&511];//表示在二級間接塊中的二級塊中的第幾個陣列元素 if (create && !i) if (i=new_block(inode->i_dev)) {//申請一個邏輯塊來存放最終的資料 ((unsigned short *) (bh->b_data))[block&511]=i;//對映下檔案中的二級塊 bh->b_dirt=1; } brelse(bh); return i;//返回邏輯塊號 }
input()
2、iput()函式是把i節點的引用遞減,如果i節點引用為1,則遞減刪除i節點,並且設定相關屬性;如果是管道檔案或者塊裝置檔案,則另外進行處理;
//放置一個i節點,減去1個i節點的引用 void iput(struct m_inode * inode) { if (!inode) return; wait_on_inode(inode); if (!inode->i_count)//如果i節點已經是0,那麼宕機 panic("iput: trying to free free inode"); if (inode->i_pipe) {//如果是管道檔案 wake_up(&inode->i_wait);//喚醒等待該管道的程序 if (--inode->i_count)//引用減去1 return;//如果還被其他程式引用,則直接返回;否則就設定一些屬性和釋放記憶體 free_page(inode->i_size);//釋放管道使用的記憶體頁面 inode->i_count=0;//對i節點屬性欄位設定 inode->i_dirt=0; inode->i_pipe=0; return; } //i節點對應的裝置號為0,引用遞減後返回;如,管道操作i節點 if (!inode->i_dev) { inode->i_count--; return; } //如果是塊裝置檔案的i節點,重新整理該裝置 if (S_ISBLK(inode->i_mode)) { sync_dev(inode->i_zone[0]);//zone[0]是裝置號 wait_on_inode(inode); } repeat: if (inode->i_count>1) {//引用遞減 返回 inode->i_count--; return; } if (!inode->i_nlinks) {//連結數為0, 表示檔案已經刪除,置檔案長度為0,釋放i節點 truncate(inode); free_inode(inode); return; } if (inode->i_dirt) {//已經更改過 write_inode(inode); /* we can sleep - so do again *///把i節點資訊寫入快取中 wait_on_inode(inode); goto repeat;//經過睡眠,要再一次確認 } inode->i_count--;//遞減返回,檔案還存在 return; }
get_empty_inode()
3、get_empty_inode(void)函式 表示在i節點陣列中查詢一個空閒的i節點,進行設定,然後返回該i節點;//從i節點表中獲取一個空閒i節點項 struct m_inode * get_empty_inode(void) { struct m_inode * inode; static struct m_inode * last_inode = inode_table; int i; do { inode = NULL; for (i = NR_INODE; i ; i--) { if (++last_inode >= inode_table + NR_INODE)//如果last_inode已經到了最後一項,則重新掃描 last_inode = inode_table; if (!last_inode->i_count) {//引用為0, inode = last_inode; if (!inode->i_dirt && !inode->i_lock)//沒有修改,沒有上鎖,就選擇這個i節點 break; } } //沒有找到i節點,列印i節點列表,供除錯,宕機 if (!inode) { for (i=0 ; i<NR_INODE ; i++) printk("%04x: %6d\t",inode_table[i].i_dev, inode_table[i].i_num); panic("No free inodes in mem"); } //檢查下是否又被上鎖 wait_on_inode(inode); //若i節點資訊修改過,則重新整理等待i節點解鎖 while (inode->i_dirt) { write_inode(inode); wait_on_inode(inode); } } while (inode->i_count);//如果i節點又被別的程序使用,那麼再找一個i節點 //對空閒i節點詳內容清零,設定引用,返回i節點指標 memset(inode,0,sizeof(*inode)); inode->i_count = 1; return inode; }
iget()
4、iget(int dev, int nr)函式 根據裝置號和i節點號得到i節點資訊;其中有個重點判斷的是該i節點是否是某個檔案系統的安裝節點。如果是,則把i節點釋放一個引用;並且重新在該檔案系統中查詢i節點(也就是根節點)
//從裝置上讀取指定i節點號的i節點資訊
//引數 dev裝置號,nr i節點號
//1、在記憶體中的i節點列表中掃描,若查詢到了,則返回i節點指標
//2、如果上面沒有找到,則從裝置上讀取指定i節點號的i節點資訊放入記憶體i節點表中,返回i節點指標
struct m_inode * iget(int dev,int nr)
{
struct m_inode * inode, * empty;
if (!dev)
panic("iget with dev==0");
empty = get_empty_inode();//得到一個空閒i節點
inode = inode_table;//i節點列表陣列
while (inode < NR_INODE+inode_table) {
if (inode->i_dev != dev || inode->i_num != nr) {//裝置號或者i節點號不相等,則看下一個
inode++;
continue;
}
//解鎖,然後再確認下裝置號和i節點號是否改變了
wait_on_inode(inode);
if (inode->i_dev != dev || inode->i_num != nr) {
inode = inode_table;
continue;
}
inode->i_count++;//引用
//如果找到的i節點是其他檔案系統的安裝節點
if (inode->i_mount) {
int i;
for (i = 0 ; i<NR_SUPER ; i++)
if (super_block[i].s_imount==inode)//看看是哪個檔案系統的安裝節點
break;
//如果沒有找到安裝在此i節點上的超級塊,則釋放申請的空閒i節點
if (i >= NR_SUPER) {
printk("Mounted inode hasn't got sb\n");
if (empty)
iput(empty);//釋放空閒i節點
return inode;
}
//找到i節點安裝的超級塊,獲取到該檔案系統的裝置號,得到nr為根節點1,重新在該檔案系統中查詢根i節點
iput(inode);
dev = super_block[i].s_dev;
nr = ROOT_INO;
inode = inode_table;
continue;
}
//如果找到的i節點不是,其他檔案系統的安裝節點,
//則釋放空節點,返回i節點
if (empty)
iput(empty);
return inode;
}
//如果在i節點列表中,沒有找到指定i節點,則自己新建一個
if (!empty)
return (NULL);
inode=empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode);//從裝置上讀取該i節點
return inode;
}
read_inode()
5、讀取裝置上指定i節點的資訊到快取中,其中要分清楚,引數中的i節點是記憶體中使用的i節點,而程式後面是讀取到硬碟上的i節點;這應該是從硬碟上的i節點中載入其資訊到記憶體中的i節點處,也就是更新i節點資訊;所以用讀i節點資訊(把i節點資訊從硬碟上讀取到快取區中,從硬碟使用的i節點更新到記憶體使用的i節點)
//從裝置上讀取指定i節點的資訊到快取中
static void read_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode);
if (!(sb=get_super(inode->i_dev)))//根據裝置號獲取到超級塊
panic("trying to read inode without dev");
//下面是計算i節點邏輯號,引導塊 超級塊 兩個點陣圖 然後就是(i節點號/每塊所擁有的i節點數)
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK;
//從磁碟上獲取到i節點資訊
if (!(bh=bread(inode->i_dev,block)))
panic("unable to read i-node block");
*(struct d_inode *)inode =
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];//這是從硬碟中i節點上獲取到資料,更新記憶體中i節點內容
brelse(bh);
unlock_inode(inode);
}
write_inode()
6、將指定i節點資訊寫入磁碟中,和上面的read_inode()函式正好相反,該函式是由記憶體中使用的i節點寫入到硬碟上使用的i節點;把記憶體中修改過的i節點資訊更新到硬碟上使用的i節點資訊;//將i節點資訊寫入快取區中
static void write_inode(struct m_inode * inode)//引數是記憶體中使用的i節點結構
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode);
if (!inode->i_dirt || !inode->i_dev) {
unlock_inode(inode);
return;
}
if (!(sb=get_super(inode->i_dev)))
panic("trying to write inode without device");
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK;//根據i節點號,找到磁碟邏輯塊上i節點位置
if (!(bh=bread(inode->i_dev,block)))//把該i節點磁碟塊讀取到快取區
panic("unable to read i-node block");
((struct d_inode *)bh->b_data)//把該快取區強轉為 磁碟使用的i節點結構
[(inode->i_num-1)%INODES_PER_BLOCK] =//根據i節點號,取餘得到在該塊磁碟上第幾個位置為i節點
*(struct d_inode *)inode;//由記憶體i節點強轉為磁碟i節點結構,因為磁碟i節點結構和記憶體i節點結構前幾位一樣
bh->b_dirt=1;
inode->i_dirt=0;
brelse(bh);
unlock_inode(inode);
}
其實上面的讀寫i節點資訊,只是在後面的轉換順序不一樣。是在d_inode和m_inode結構體之間做文章的,因為兩個結構體中前7項是一樣的,也就是說前面7項是可以共用,可以改變的,而m_inode結構體中後面幾項d_inode結構體中是沒有的(其實這也是確定i節點的唯一性),所以也就不存在是否同步,是否一樣。
讀i節點順序是:*(struct d_inode *)inode = ((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK];從硬碟對映的快取區中讀取到i節點資訊到記憶體i節點結構體中;
寫i節點順序是:((struct d_inode *)bh->b_data)[(inode->i_num-1)%INODES_PER_BLOCK] =*(struct d_inode *)inode;把i節點結構體內容從記憶體中寫入到硬碟對映的快取區中使用的i節點結構體(其實也就是硬碟上使用的i節點);
若有不正確之處,望大家指正,共同學習!謝謝!!