如何實現(xiàn)一個簡單的日志系統(tǒng)
時間:2021-09-26 13:40:15
手機看文章
掃描二維碼
隨時隨地手機看文章
[導(dǎo)讀]日志本文來聊聊文件系統(tǒng)中的日志系統(tǒng),來看一個簡單的日志系統(tǒng)是如何實現(xiàn)的。本文是接著前面的xv6系列,用到的一些前導(dǎo)知識不再說明,沒看的可以先看一下。文件系統(tǒng)設(shè)計中通常要考慮錯誤恢復(fù),這是因為文件系統(tǒng)會涉及對磁盤的多次寫操作,如果在寫的過程中系統(tǒng)崩潰了,就會使得磁盤上的文件系統(tǒng)處于...
日志
本文來聊聊文件系統(tǒng)中的日志系統(tǒng),來看一個簡單的日志系統(tǒng)是如何實現(xiàn)的。本文是接著前面的 xv6 系列,用到的一些前導(dǎo)知識不再說明,沒看的可以先看一下。文件系統(tǒng)設(shè)計中通常要考慮錯誤恢復(fù),這是因為文件系統(tǒng)會涉及對磁盤的多次寫操作,如果在寫的過程中系統(tǒng)崩潰了,就會使得磁盤上的文件系統(tǒng)處于不一致的錯誤狀態(tài)。日志就是設(shè)計來解決因為系統(tǒng)崩潰導(dǎo)致的錯誤問題,本文就 來講解怎么實現(xiàn)一個簡單的日志系統(tǒng)。在 的日志系統(tǒng)中,文件操作方面的系統(tǒng)調(diào)用并不會直接對磁盤進行寫操作,而是把對磁盤寫操作描述包裝成一個日志寫在磁盤中,當(dāng)該系統(tǒng)調(diào)用執(zhí)行完成之后,再提交一個記錄到磁盤上。為什么日志可以解決文件系統(tǒng)操作中出現(xiàn)的崩潰呢?如果崩潰發(fā)生在提交之前,那么磁盤上的日志文件就不會被標(biāo)記為已完成,恢復(fù)系統(tǒng)的代碼就會忽視它,磁盤的狀態(tài)就好像寫操作從未進行一樣。如果是在提交之后崩潰的,恢復(fù)程序會重演所有的寫操作。在任何一種情況下,日志文件都使得磁盤操作對于系統(tǒng)崩潰來說是原子操作:在恢復(fù)之后,要么所有的寫操作都完成了,要么一個寫操作都沒有完成。上面的理論大都來自 文檔,我們能了解到,最為重要的是實現(xiàn)寫操作的原子性,那么怎樣實現(xiàn)呢? 在磁盤上分配了一片日志區(qū),假如現(xiàn)在內(nèi)存中有一個緩存塊準(zhǔn)備同步到磁盤區(qū)域 A, 并不立即將該緩存塊的數(shù)據(jù)寫到磁盤區(qū)域 A,而是先寫到磁盤的日志區(qū)(提交)。如果沒有問題則將日志區(qū)的數(shù)據(jù)寫到相應(yīng)的磁盤區(qū)域 A。如果有問題,在提交之前發(fā)生了崩潰,則恢復(fù)代碼忽略日志信息,區(qū)域 A 根本就沒進行過寫操作,當(dāng)然就能夠保證數(shù)據(jù)的一致性。如果在提交之后發(fā)生了崩潰,則恢復(fù)代碼將日志區(qū)的數(shù)據(jù)重新寫到磁盤區(qū)域 A,也保證了數(shù)據(jù)的一致性。日志區(qū)也需要相應(yīng)的數(shù)據(jù)結(jié)構(gòu)來組織管理,相關(guān)的結(jié)構(gòu)定義如下:結(jié)構(gòu)定義
超級塊
struct?superblock?{
??uint?size;?????????//?Size?of?file?system?image?(blocks)?文件系統(tǒng)大小,也就是一共多少塊
??uint?nblocks;??????//?Number?of?data?blocks??數(shù)據(jù)塊數(shù)量
??uint?ninodes;??????//?Number?of?inodes.???//i結(jié)點數(shù)量
??uint?nlog;?????????//?Number?of?log?blocks???//日志塊數(shù)量??
??uint?logstart;?????//?Block?number?of?first?log?block??//第一個日志塊塊號?
??uint?inodestart;???//?Block?number?of?first?inode?block??//第一個i結(jié)點所在塊號
??uint?bmapstart;????//?Block?number?of?first?free?map?block??//第一個位圖塊塊號
};
文件系統(tǒng)的超級塊,超級塊中記錄了文件系統(tǒng)的元信息,比如上述 的超級塊記錄了數(shù)據(jù)塊、i 結(jié)點、日志塊的數(shù)量和第一塊的塊號。 文件系統(tǒng)的總體布局如下:日志頭
#define?MAXOPBLOCKS??10??//?max?#?of?blocks?any?FS?op?writes
#define?LOGSIZE?(MAXOPBLOCKS*3)??//?max?data?blocks?in?on-disk?log
struct?logheader?{?????//日志頭部
??int?n;???????????????
??int?block[LOGSIZE];
};
日志頭用來記錄每次日志的大小和位置關(guān)系信息。 來記錄每次日志使用的空間大小,日志空間的總大小記錄在超級塊中(大小的單位是塊),同時 也規(guī)定每次日志使用的塊數(shù)也不能超過 。 是一個 型數(shù)組,元素個數(shù)最多為 ,用來記錄位置關(guān)系。寫入磁盤是先寫入日志區(qū),再寫到磁盤的其他區(qū)域。這個日志區(qū)的磁盤塊和其他區(qū)域的磁盤之間需要有一個映射關(guān)系,這個關(guān)系就記錄在 數(shù)組中。舉個例子: 表示日志塊 記錄的數(shù)據(jù)應(yīng)放在 號磁盤塊中。struct?log?{
??struct?spinlock?lock;
??int?start;????//日志區(qū)第一塊塊號
??int?size;?????//日志區(qū)大小
??int?outstanding;?//?有多少文件系統(tǒng)調(diào)用正在執(zhí)行
??int?committing;??//?正在提交
??int?dev;?????//設(shè)備,即主盤還是從盤,文件系統(tǒng)在從盤
??struct?logheader?lh;???//日志頭
};
struct?log?log;
這個結(jié)構(gòu)體只存在于內(nèi)存,用來記錄當(dāng)前的日志信息。這個日志信息也是一個公共資源要避免競爭條件所以配了一把鎖。 三個屬性值從超級塊中讀取。其他的信息見注釋,具體含義后面慢慢講解。下面直接來看日志的函數(shù)實現(xiàn):函數(shù)實現(xiàn)
void?readsb(int?dev,?struct?superblock?*sb)??//讀超級塊
{
??struct?buf?*bp;
??bp?=?bread(dev,?1);??//讀取超級塊數(shù)據(jù)到緩存塊
??memmove(sb,?bp->data,?sizeof(*sb));??//移動數(shù)據(jù)
??brelse(bp);???//釋放緩存塊
}
這個函數(shù)用來讀取超級塊的內(nèi)容,超級塊在第一塊,第零塊是引導(dǎo)塊。調(diào)用 將數(shù)據(jù)從磁盤讀取到緩存塊中,然后將緩存塊中超級塊的數(shù)據(jù)復(fù)制一份到內(nèi)存中定義的超級塊數(shù)據(jù)結(jié)構(gòu)中去,最后再釋放緩存塊的鎖,因為 調(diào)用 獲取了鎖,使用完該緩存塊就該釋放,詳見磁盤那篇文章void?initlog(int?dev)
{
??if?(sizeof(struct?logheader)?>=?BSIZE)
????panic("initlog:?too?big?logheader");
??struct?superblock?sb;???//定義局部變量超級塊sb
??initlock(