Reiser文件系統結構(4)

日誌(Journal

Reiser文件系統的日誌是一些連續的磁盤塊,記錄了文件系統的所有事務。文件系統每次有修改時,都會把一系列操作(即爲了維持文件系統的一致性,必須原子地完成的那些操作)組合成爲事務,並首先記錄到日誌裏。在遲一些的時候,這些事務被刷新,並標記爲成功。

日誌的大小是固定的。在2.4.x版本Linux實現裏,日誌大小是8192個塊,加一個日誌頭信息塊。日誌本身包括一個變長的事務列表和一個日誌頭信息,頭信息在日誌的最後。一個事務可以有3個塊那麼大,但是日誌的頭信息總是1個塊大小。整個日誌是一個環形緩衝區,即如果寫到了最後的塊,那麼下次就從第一個塊開始寫。

經常有人認爲Reiser文件系統的日誌只記錄系統元數據,其實不是的。日誌的作用的確是保證元數據的完整性。但是Reiser文件系統記錄下整個磁盤塊的內容,並提交成日誌。和目錄一樣,狀態數據和小文件都是直接存儲在葉子節點裏,同時部分數據還可能存在於日誌裏,這樣可以在適當的時候恢復它們以前的版本。

 

日誌頭信息(Journal Header

日誌頭信息佔用日誌的最後一個塊,描述第一個未刷新的事務的位置。在下面的例子裏,第一個未刷新的塊是18號,並且總共有8192個日誌塊。所以,日誌頭信息塊編號是8210,日誌頭信息只佔用12字節,塊裏其他空間沒有使用。

 

Name

Size

Description

Last flush ID

4

上次刷新的事務id

Unflushed offset

4

下一個需要刷新的事務所在的塊編號

Mount ID

4

已刷新事務的mount ID

offset域指向的事務必須比已刷新的事務有更高的事務idmount id,這樣才被認爲是未刷新的事務,否則所有的事務都被認爲已經刷新,而offset域指向的塊只是說明從哪裏開始記錄新事務的日誌。

示例:

00000000 e2 74 02 00 24 1c 00 00 1d 01 00 00 12 00 00 00  ât..$...........

 

Last flush ID: 160994

Unflushed offset: 7204 blocks

Mount ID: 285

在上例裏,第一個未刷新的事務位於塊7222上(日誌始於18號塊)。但是由於7222號塊上沒有日誌描述(見下面),所以實際上沒有未刷新的塊。

 

事務(Transactions

事務描述了文件系統的更改。文件系統樹裏需要新增或修改塊時,不是直接進行,而是首先寫到日誌裏,然後映射到文件系統裏的實際位置。

一個事務包含一個事務描述塊(transaction description block),一系列數據塊,和最後的提交塊(commit block)。所有的塊在日誌裏都是連續的。

 

描述塊(Description block

描述塊裏包含事務和mount id,事務擁有的數據塊數,一個魔幻數,和(部分)塊映射。

 

Name

Size

Description

Transaction ID

4

事務id

Len

4

事務的數據塊數

Mount ID

4

Mount ID

Real blocks

Block size - 24

事務的塊映射

Magic

12

魔幻數,“ReIsErLB

Real blocks”域的大小理論上依賴於塊大小。頭12個字節是事務id和長度,最後12個字節是魔幻數,中間的部分就是塊映射區“Real blocks”。不過,在Linux版本2.4.x實現裏,描述塊的定義有:

__u32 j_realblock[JOURNAL_TRANS_HALF];

JOURNAL_TRANS_HALF是值爲1018的常數,所以決定了Linux下塊大小必須是4096

實際的塊映射是這樣的:“Real blocks”域是一個塊編號數組,把事務裏的每個數據塊映射到文件系統裏的某個塊上。如果我們把數組裏的每個值編號爲r0r1...rn,那麼事務的第0個數據塊包含了塊r0修改後的數據,第1個數據塊包含塊r1修改後的數據,以此類推。如果這裏的“Real blocks”域不夠用,那麼會使用提交塊(見下面)裏的空間作爲補充。一個事務最多能有2×(塊大小 - 24/ 4個數據塊(塊大小爲4K時結果爲2036),但是超級塊裏會作出實際的限制。

 

提交塊(Commit block

提交塊是一個事務的結尾,它包含事務id和長度的副本,塊的最後有一個16字節的保留域,暫時沒有使用。塊的其他空間用於存儲前面放不下的塊編號映射。

 

Name

Size

Description

Transaction ID

4

事務id

Len

4

事務的數據塊數

Real blocks

Block size - 24

事務的塊映射

Digest

16

事務所有塊的摘要,未使用

示例:

下面描述了一個過時了的事務,起始塊是7243(描述塊),4個數據塊(7244-7247),結束塊是7248(提交塊)。這裏只顯示了描述塊,其他塊的數據不重要。

00000000 1b 6e 02 00 04 00 00 00 1b 01 00 00 90 22 00 00  .n..........."..

00000010 07 f7 00 00 aa 22 00 00 10 00 00 00 00 00 00 00  .÷..ª"..........

00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

...

00000ff0 00 00 00 00 52 65 49 73 45 72 4c 42 00 00 00 00  ....ReIsErLB....

 

Transaction ID: 159259

Length: 4 blocks

Mount ID: 283

Real blocks[0]: 8848

Real blocks[1]: 63239

Real blocks[2]: 8874

Real blocks[3]: 16

Magic: ReIsErLB

這個例子裏的塊映射是:當事務提交/刷新的時候,把塊7244寫到塊8848,塊7245寫到塊63239,塊7246寫到塊8874,塊7247寫到塊16(超級塊)。

注意:很多操作都需要更新超級塊,比如空閒塊數目更新了,或文件系統樹的高度改變了。所以超級塊的副本在很多日誌事務裏都能找到,這樣如果通過匹配標誌字符串(比如“ReIsEr2Fs”)來找尋超級塊,可能會找到很多其他的塊。實際上超級塊“應該”是這些含有標誌字符串的塊中的頭一個。

 

Reiser文件系統總結

爲了能訪問到特定的文件,我們必須搜索文件系統的目錄樹。Reiser文件系統的根目錄(root directory)的key總是{1200}。目錄下的文件的key可以在目錄項的頭信息裏找到。由於Reiser文件系統的key把目錄id放在首位,同一個目錄下的對象會組織到一起。這樣就使key的搜索更加局域化(locally),而不需要每次都從根目錄開始。

key {ab00}總是表示狀態項,緊接着就是它描述的目錄或文件對象。狀態項裏有目標對象的字節大小,再利用後續項頭信息裏的大小信息,可以重建起目標目錄/文件的所有key,和每部分的位置。在大部分情況下,這些項在磁盤上是連續存放的。

下面的3個例子顯示了3中不同的文件:非常小的文件,只有一個狀態項和尾數據;大一些的文件,有一個間接項;和非常大的文件,跨越幾個間接項。我們仍用前面例子裏使用的分區,它是SuSe Linux 8.0系統上裝載到“/var”下的一個Reiser文件系統分區。

 

例子1:小文件

第一個例子的文件只包含一個狀態項(stat item)和一個直接項(direct item)。文件名是“/var/log/y2start.log-initial”。根目錄“/var”的key{1200},存儲於塊8416。文件夾“log”的key{21300},也存儲於塊8416上。文件“y2start.log-initial”的key{13163300}。通過查看塊8482,我們發現這個key存儲於葉子節點24224上。key {13163300}{13163312}的頭信息如下:

00000090 0d 00 00 00 61 06 00 00 00 00 00 00 00 00 00 00  ....a...........

000000a0 ff ff 2c 00 a4 0b 01 00 0d 00 00 00 61 06 00 00  ÿÿ,.¤.......a...

000000b0 01 00 00 00 00 00 00 20 ff ff f0 00 b4 0a 01 00  ....... ÿÿð.´...

Key: {13, 1633, 0, 0}

Count: 0xffff

Length: 44 bytes

Location: byte 2980 (0xba4)

Version: 1 (new)

Key: {13, 1633, 1, 2}

Count: 0xffff

Length: 240 bytes

Location: byte 2740 (0xab4)

Version: 1 (new)

該塊(24224)的偏移2740 (0xab4)字節處,我們找到了文件的直接項(狀態項位於2980 (0xba4)字節處):

00000ab0             65 6e 76 0a 65 63 68 6f 20 59 32 44      env.echo Y2D

00000ac0 45 42 55 47 20 28 29 0a 6d 65 6d 69 6e 66 6f 20  EBUG ().meminfo

00000ad0 31 20 3d 20 4d 65 6d 3a 20 31 30 33 33 34 35 36  1 = Mem: 1033456

00000ae0 20 38 35 39 37 36 20 39 34 37 34 38 30 20 30 20   85976 947480 0

00000af0 36 34 32 34 20 35 37 31 37 32 0a 69 53 65 72 69  6424 57172.iSeri

00000b00 65 73 3d 31 0a 68 76 63 5f 63 6f 6e 73 6f 6c 65  es=1.hvc_console

00000b10 3d 31 0a 58 31 31 69 3d 0a 4d 65 6d 54 6f 74 61  =1.X11i=.MemTota

00000b20 6c 3d 31 30 33 33 34 35 36 0a 66 62 64 65 76 5f  l=1033456.fbdev_

00000b30 6f 6b 3d 31 0a 75 70 64 61 74 65 3d 0a 58 56 65  ok=1.update=.XVe

00000b40 72 73 69 6f 6e 3d 34 0a 58 53 65 72 76 65 72 3d  rsion=4.XServer=

00000b50 66 62 64 65 76 0a 78 73 72 76 3d 58 46 72 65 65  fbdev.xsrv=XFree

00000b60 38 36 0a 73 63 72 65 65 6e 3d 66 62 64 65 76 0a  86.screen=fbdev.

00000b70 6d 65 6d 69 6e 66 6f 20 32 20 3d 20 4d 65 6d 3a  meminfo 2 = Mem:

00000b80 20 31 30 33 33 34 35 36 20 39 32 34 30 34 20 39   1033456 92404 9

00000b90 34 31 30 35 32 20 30 20 38 32 33 32 20 36 30 35  41052 0 8232 605

00000ba0 31 36 0a 00 a4 81 05 00 01 00 00 00 ef 00 00 00  16..¤.......ï...

00000bb0 00 00 00 00 00 00 00 00 00 00 00 00 25 15 3e 3d  ............%.>=

00000bc0 25 15 3e 3d 25 15 3e 3d 08 00 00 00 d5 02 00 00  %.>=%.>=....Õ...

Mode: S_IFREG (regular file), -rw-r--r--

Num. links: 1

Size: 239

UID: 0

GID: 0

A/M/Ctimes: 07/23/2002 21:47:01

Blocks: 8

Gen: 725

值得注意的是,狀態項顯示了文件的實際大小爲239字節,而直接項卻佔用了塊內的240字節,所以第2979 (0xba3)字節並不是文件的數據。這個差異也許可以用實現中的字節對齊來解釋。

 

例子2:有間接項的文件

文件“"/var/log/SaX.log”有7121字節長,不能放在一個直接項裏,必須分開到2個無格式塊裏,並由一個間接項來描述。文件的key{13, 1490, 0, 0},從塊8482裏我們找到它位於葉子節點27444裏。對象頭信息如下:

00000040                         0d 00 00 00 d2 05 00 00          ....Ò...

00000050 00 00 00 00 00 00 00 00 ff ff 2c 00 a4 0b 01 00  ........ÿÿ,.¤...

00000060 0d 00 00 00 d2 05 00 00 01 00 00 00 00 00 00 10  ....Ò...........

00000070 00 00 08 00 9c 0b 01 00                              ........

Key: {13, 1490, 0, 0}

Count: 0xffff

Length: 44 bytes

Location: byte 2980 (0xba4)

Version: 1 (new)

Key: {13, 1490, 1, 1}

Count: 0

Length: 8 bytes

Location: byte 2972 (0xb9c)

Version: 1 (new)

間接塊和狀態項爲:

00000b90                                     12 52 00 00              .R..

00000ba0 13 52 00 00 a4 81 05 00 01 00 00 00 d1 1b 00 00  .R..¤.......Ñ...

00000bb0 00 00 00 00 00 00 00 00 00 00 00 00 3f aa 4a 3d  ............?ªJ=

00000bc0 bd aa 4a 3d bd aa 4a 3d 10 00 00 00 54 05 00 00  ½ªJ=½ªJ=....T...

Mode: S_IFREG (regular file), -rw-r--r--

Num. links: 1

Size: 7121

UID: 0

GID: 0

C time: Fri Aug 2 10:50:23 2002

M/Atimes: Fri Aug 2 10:52:29 2002

Blocks: 10

Gen: 1364

Block 1: 21010

Block 2: 21011

該文件的數據位於塊2101021011上。塊21010上有4096字節,塊21011上只有3025字節。(譯註:前面第一個key代表狀態項,起始位置是0xba4字節;第二個key代表間接項,起始位置是0xb9c字節。注意葉子節點裏,key的順序與項的順序是相反的。不過有幾點疑問。前面說過間接項頭信息裏Count域“表示最後一個無格式塊的空閒字節數”,但是此處等於0;狀態項裏的“Blocks”域是“文件佔用的塊數”,此處值爲10,不知是怎麼算的。

 

例子3:大文件

文件“"/var/lib/rpm/fileindex.rpm”的大小超過了11MB,單個間接項已經不能描述它了。它的key{4, 7, 0, 0},位於塊16822裏。不過塊16822只包含了它的狀態項。它的間接項分佈於3個塊裏:key {4, 7, 1, 1}在塊13286裏,key {4, 7, 4145153, 1}在塊20171裏,key {4, 7, 8290305, 1}在塊20987裏。塊13286裏的間接項爲:

00000010                         04 00 00 00 07 00 00 00          ........

00000020 01 00 00 00 00 00 00 10 00 00 d0 0f 30 00 01 00  ..........Ð.0...

Key: {4, 7, 1, 1}

Count: 0

Length: 4048 bytes

Location: byte 48 (0x30)

Version: 1 (new)

接下來是1012個指向無格式塊的指針,塊20171的結構也是這樣。塊20987上的間接項只有830個指針,佔用3320字節。注意這3key裏的offset域是怎樣來的:

1 + (1012 pointers * 4096 bytes blocksize) = 4145153

4145153 + (1012 pointers * 4096 bytes blocksize) = 8290305

  

原文《The structure of the Reiser file system

http://homes.cerias.purdue.edu/~florian/reiser/reiserfs.php

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章