Ext2文件系統(tǒng)徹底分析 | 磁盤(pán)空間分配

在實(shí)際寫(xiě)數(shù)據(jù)到磁盤(pán)之前需要分配磁盤(pán)上的空間。這里的寫(xiě)數(shù)據(jù)包括寫(xiě)文件數(shù)據(jù)、在目錄中創(chuàng)建文件和添加擴(kuò)展屬性等等。但凡需要存儲(chǔ)新數(shù)據(jù)的場(chǎng)景都需要分配磁盤(pán)空間。分配磁盤(pán)空間的主要功能在函數(shù)ext2_get_blocks中實(shí)現(xiàn),該函數(shù)的原型如下所示:

static int ext2_get_blocks(struct inode *inode,
                           sector_t iblock, unsigned long maxblocks,
                           struct buffer_head *bh_result,
                           int create)

該函數(shù)原型中,重點(diǎn)需要說(shuō)明的是iblock參數(shù),該參數(shù)表示文件的邏輯位置,位置以文件系統(tǒng)的塊大小為單位,值為文件系統(tǒng)的以0為起始位置邏輯地址。舉個(gè)簡(jiǎn)單的例子,加入文件系統(tǒng)格式化時(shí)塊大小是2K,而此時(shí)寫(xiě)入數(shù)據(jù)的偏移為4K,那么此時(shí)iblock就是2。也就是說(shuō),該函數(shù)通過(guò)數(shù)據(jù)在文件中的邏輯位置計(jì)算需要分配多少磁盤(pán)空間。


圖1 磁盤(pán)空間分配主流程

函數(shù)ext2_get_blocks的主流程如圖1所示,該函數(shù)主要完成三方面的工作:

  1. 計(jì)算并獲取存儲(chǔ)路徑,我們知道文件數(shù)據(jù)是通過(guò)間接塊的方式存儲(chǔ)的,因此這里主要是根據(jù)數(shù)據(jù)邏輯地址計(jì)算出其存儲(chǔ)的路徑情況,并獲得該路徑。
  2. 計(jì)算需要分配的塊的數(shù)量和期望的磁盤(pán)物理位置。
  3. 分配磁盤(pán)空間,計(jì)算出需要的磁盤(pán)空間的數(shù)量后,最后該函數(shù)調(diào)用ext2_alloc_branch來(lái)分配需要的磁盤(pán)空間,具體就是將空間管理的位圖置位。
    圖2 Ext2間接塊數(shù)組組織形式

為了容易理解磁盤(pán)整個(gè)分配磁盤(pán)空間的流程,我們先回顧一下Ext2的間接塊結(jié)構(gòu),如圖2所示(具體解釋請(qǐng)參考本號(hào)歷史文章)。為了理解更清楚一些,我們假設(shè)請(qǐng)求的數(shù)據(jù)位置需要二級(jí)間接塊,因此,我們將該關(guān)系放大,如圖3所示。我們知道對(duì)于Ext2來(lái)說(shuō),其地址是32位的,因此在間接塊中的數(shù)據(jù)其實(shí)可以理解為無(wú)符號(hào)整型的大數(shù)組。其中數(shù)組中的每一項(xiàng)就是一個(gè)下一級(jí)磁盤(pán)數(shù)據(jù)塊的地址。這樣我們根據(jù)數(shù)據(jù)在文件中的邏輯地址就可以計(jì)算出來(lái)需要幾級(jí)間接塊及位置數(shù)據(jù)在該間接塊中存放的位置。

圖3 二級(jí)間接塊路徑示例

有了間接塊需要的數(shù)據(jù),我們?cè)诮Y(jié)合數(shù)據(jù)本身需要的磁盤(pán)空間,就可以計(jì)算出本次請(qǐng)求需要申請(qǐng)的磁盤(pán)空間。最后就是根據(jù)這些來(lái)分配磁盤(pán)空間了。下面我們分布詳細(xì)介紹一下各個(gè)流程的實(shí)現(xiàn)細(xì)節(jié)。

計(jì)算存儲(chǔ)路徑

所謂計(jì)算存儲(chǔ)路徑是指計(jì)文件數(shù)據(jù)邏輯地址在間接塊中存儲(chǔ)的物理地址表示。仍然以上述圖3為例,對(duì)于二級(jí)間接塊可存儲(chǔ)的情況,在分配磁盤(pán)空間之前需要計(jì)算出其在一級(jí)間接塊和二級(jí)間接塊中的位置。這樣,在后續(xù)的數(shù)據(jù)查找流程,根據(jù)這些間接塊中存儲(chǔ)的地址信息就可以找到文件某個(gè)位置的數(shù)據(jù)在磁盤(pán)的物理地址。
函數(shù)ext2_block_to_path用于計(jì)算數(shù)據(jù)在間接塊數(shù)組中存放的路徑。該函數(shù)的功能主要是根據(jù)邏輯地址計(jì)算出在深度及每一層的位置。前文我們已經(jīng)知道文件數(shù)據(jù)的放置方式,結(jié)合圖3以比較清楚的理解本函數(shù)的代碼。這里根據(jù)數(shù)據(jù)的邏輯地址分為4種情況,分別如下:

  1. ** 不需要間接塊 **: 也就是數(shù)據(jù)目的位置(以文件塊大小為單位)在12以?xún)?nèi),則說(shuō)明是直接引用,不需要間接塊,此時(shí)在數(shù)組的前12個(gè)元素中的一個(gè)。
  2. ** 一級(jí)間接塊 **: 數(shù)據(jù)范圍在一級(jí)間接塊可表示的范圍內(nèi),此時(shí)表示路徑的數(shù)組的第一個(gè)元素是inode數(shù)組中的第12個(gè)元素,而第二個(gè)元素則是在間接塊中的具體位置。比如i_block是18,此時(shí)通過(guò)直接尋址無(wú)法滿(mǎn)足要求,因此需要一級(jí)間接塊。這樣,offsets中第一個(gè)元素的值是12,表示是一級(jí)間接塊;offsets的第二個(gè)元素是6,因?yàn)橹苯铀饕梢员硎?2個(gè)數(shù)據(jù)塊,因此在間接塊中的分別可以存儲(chǔ)從第13到256+12的數(shù)據(jù)范圍,對(duì)于位置為18的數(shù)據(jù)在間接塊的位置就是6。
  3. ** 二三級(jí)間接塊 **: 以此類(lèi)推,根據(jù)邏輯地址的大小可能會(huì)需要二級(jí)甚至三級(jí)間接塊,依照這種算法可以計(jì)算出每一級(jí)間接塊的位置。這里不在贅述。

下面是本函數(shù)的代碼,本文加了一些注釋?zhuān)a本身比較簡(jiǎn)單,本文不在贅述。這里需要注意的是,除了返回深度和每一層的位置外,還會(huì)返回在最后的間接塊上可容納的地址的數(shù)量。比如計(jì)算出來(lái)在最后一級(jí)間接塊的位置是250,那么最多可容納6個(gè)地址。

static int ext2_block_to_path(struct inode *inode,
                        long i_block, int offsets[4], int *boundary)
{
        int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);    //這里計(jì)算出每個(gè)間接塊可以存儲(chǔ)多少地址
        int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);  //塊大小的位數(shù)
        const long direct_blocks = EXT2_NDIR_BLOCKS,    //直接塊的數(shù)量
                indirect_blocks = ptrs,                 //間接塊可存儲(chǔ)地址的數(shù)量
                double_blocks = (1 << (ptrs_bits * 2)); //二級(jí)間接塊可存儲(chǔ)地址的數(shù)量
        int n = 0; 
        int final = 0; 

        if (i_block < 0) { 
                ext2_msg(inode->i_sb, KERN_WARNING,
                        "warning: %s: block < 0", __func__);
        } else if (i_block < direct_blocks) {
                offsets[n++] = i_block;
                final = direct_blocks;
        } else if ( (i_block -= direct_blocks) < indirect_blocks) {
                offsets[n++] = EXT2_IND_BLOCK;
                offsets[n++] = i_block;
                final = ptrs;
        } else if ((i_block -= indirect_blocks) < double_blocks) {
                offsets[n++] = EXT2_DIND_BLOCK;
                offsets[n++] = i_block >> ptrs_bits;
                offsets[n++] = i_block & (ptrs - 1);
                final = ptrs;
        } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
                offsets[n++] = EXT2_TIND_BLOCK;
                offsets[n++] = i_block >> (ptrs_bits * 2);
                offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
                offsets[n++] = i_block & (ptrs - 1);
                final = ptrs;
        } else {
                ext2_msg(inode->i_sb, KERN_WARNING,
                        "warning: %s: block is too big", __func__);
        }
        if (boundary)
                *boundary = final - 1 - (i_block & (ptrs - 1));

        return n;
}

獲取存儲(chǔ)路徑

前面邏輯計(jì)算出了深度和每一級(jí)間接塊應(yīng)該存儲(chǔ)的信息,但具體的間接塊目前處于什么情況并不清楚。仍然以上述二級(jí)間接塊為例,可能會(huì)出現(xiàn)如下幾種情況:

  1. 用戶(hù)訪問(wèn)的數(shù)據(jù)位置所需要的間接塊已經(jīng)全部分配了
  2. 一級(jí)間接塊已經(jīng)具備了,但二級(jí)間接塊不具備
  3. 所有間接塊都不具備

因此,這里的工作就是根據(jù)當(dāng)前信息及上一步計(jì)算出的信息進(jìn)行綜合判斷,確定已經(jīng)具備的間接塊,并返回關(guān)鍵信息,為后續(xù)流程分配磁盤(pán)空間做準(zhǔn)備。具體實(shí)現(xiàn)在函數(shù)ext2_get_branch中。該函數(shù)的輸入是上一個(gè)函數(shù)的計(jì)算結(jié)果,輸出是需要分配的間接塊的信息(chain)。

static Indirect *ext2_get_branch(struct inode *inode,
                                 int depth,
                                 int *offsets,
                                 Indirect chain[4],
                                 int *err)

分配磁盤(pán)空間

完成間接塊情況分析之后,再經(jīng)過(guò)簡(jiǎn)單的計(jì)算,就可以計(jì)算出總共需要分配的磁盤(pán)塊的數(shù)量。然后就可以調(diào)用ext2_alloc_branch函數(shù)分配磁盤(pán)空間了。該函數(shù)主要調(diào)用了2個(gè)函數(shù),具體如圖3所示。其中ext2_alloc_blocks用戶(hù)分配磁盤(pán)塊,本質(zhì)是將管理磁盤(pán)空間的位圖的對(duì)應(yīng)位進(jìn)行置位操作;另外一個(gè)函數(shù)sb_getblk用于從磁盤(pán)讀取該塊的數(shù)據(jù),并進(jìn)行初始化。


圖4 空間分配函數(shù)主流程

初始化的目的比較明確,因?yàn)殚g接塊用來(lái)存儲(chǔ)地址信息,如果是從磁盤(pán)讀取的新間接塊數(shù)據(jù)可能是未知值,因此需要清零操作,并且完成本請(qǐng)求地址的初始化操作。

至此,磁盤(pán)空間分配的主要流程執(zhí)行完成,但仍然有一些小的處理流程,比如更新inode中的記錄最后一次分配位置、更新時(shí)間和將inode變臟等等。這些細(xì)節(jié)讀者可以自行閱讀代碼理解。

另外,為了簡(jiǎn)化,本文有些內(nèi)容沒(méi)有介紹,比如預(yù)留窗口,還有就是跨越間接塊的處理等問(wèn)題。這些問(wèn)題相對(duì)復(fù)雜一些,我們后續(xù)文章會(huì)繼續(xù)介紹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 為了便于理解Ext2文件系統(tǒng)寫(xiě)數(shù)據(jù)的流程,本文先從整個(gè)Linux文件系統(tǒng)的角度分析一下寫(xiě)數(shù)據(jù)的流程,因此本文包含了...
    SunnyZhang的IT世界閱讀 887評(píng)論 0 1
  • 1. 基礎(chǔ)知識(shí) 1.1、 基本概念、 功能 馮諾伊曼體系結(jié)構(gòu)1、計(jì)算機(jī)處理的數(shù)據(jù)和指令一律用二進(jìn)制數(shù)表示2、順序執(zhí)...
    yunpiao閱讀 5,794評(píng)論 1 22
  • 一個(gè)基本的計(jì)算機(jī)系統(tǒng)由“硬件”和“軟件”組成,一臺(tái)Linux設(shè)備,主要的組成如下圖所示: 一般情況下,我們所說(shuō)的L...
    時(shí)待吾閱讀 1,795評(píng)論 0 16
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí),會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,973評(píng)論 0 9
  • 1、感賞媽媽給了我六百塊錢(qián),讓我拿去花。哈哈,有錢(qián)錢(qián)花啦。哇塞,是誰(shuí)這么棒呀?當(dāng)然是歡寶寶啦。媽媽最近老說(shuō)給...
    o糖果罐o閱讀 128評(píng)論 0 1

友情鏈接更多精彩內(nèi)容