十一週學習筆記
阿新 • • 發佈:2021-11-21
塊裝置I/O和緩衝區管理
塊裝置I/O緩衝區
- 大多數檔案系統使用I/O緩衝來減少進出儲存裝置的物理I/O數量一合理設計的I/O緩衝方案可顯著提高檔案I/O效率並增加系統吞吐量。
- 檔案系統使用一系列IO緩衝區作為塊裝置的快取記憶體。當程序試圖讀取(dev,blk)標識的磁碟塊時,它首先在緩衝區快取中搜索分配給磁碟塊的緩衝區。如果該緩衝區存在並且包含有效資料,那麼它只需從緩衝區中讀取資料,而無須再次從磁碟中讀取資料塊。如果該緩衝區不存在,它會為磁碟塊分配一個緩衝區,將資料從磁碟讀入緩衝區,然後從緩衝區讀取資料。當某個塊被讀入時,該緩衝區將被儲存在緩衝區快取中,以供任意程序對同一個塊的下一次讀/寫請求使用。同樣,當程序寫入磁碟塊時,它首先會獲取一個分配給該塊的緩衝區。然後,它將資料寫入緩衝區,將緩衝區標記為髒,以延遲寫人,並將其釋放到緩衝區快取中。由於髒緩衝區包含有效的資料,因此可以使用它來滿足對同一塊的後續讀/寫請求,而不會引起實際磁碟L/O。髒緩衝區只有在被重新分配到不同的塊時才會寫入磁碟。
- 定義一個bread(dev, blk)函式,它會返回一個包含有效資料的緩衝區(指標)。
BUFFER *bread(dev,blk) // return a buffer containing valid data { BUFFER *bp =» getblk(dev,blk)} // get a buffer for (dev,blk) if (bp data valid) return bp; bp->opcode = READ; // issue READ operation start_lo(bp): // ntart I/O on device wait for I/O completion; }
- 從緩衝區讀取資料後,程序通過brelse(hp)格緩衝區釋放回緩衝區快取。同理,定義一個 write_block(dev, blk, data)函式:
write_block(devf blk, data) BUFFER *bp = bread(dev,blk); // read in the disk block first write data to bp; (synchronous write)? bwrite(bp) : dwrite(bp); bwrite(BUFFER *bp)( bp->opcode = WRITE; start_io(bp); wait forI/O completion; brelse(bp); // release bp dwrite(BUFFER *bp)( mark bp dirty for delay_write; brelse(bp); // release bp
Unix I/O緩衝區管理演算法
- (1)I/O緩衝區:核心中的一系列NBUF緩衝區用作緩衝區快取。每個緩衝區用一個結構體表示。
typdef struct buf{ struct buf *next_free; //freelist pointer struct buf *next_dev; //dev_list pointer int dev,blk; //assigned disk block; int opcode; //READ|WRITE int dirty; //buffer data modified int async; //ASYNC write flag int valid; //buffer data valid int busy; //buffer is in use int wanted; some process needs this buffer struct, semaphore lock=l ; //buffer locking semaphore; value=L struct semaphore iodone=0; //for process to wait for I/O completion; char buf[BLKSIZE]; //block data area } BUFFER; BUFFER buf[NBUF], *freelist; // NBUF buffers and free buffer list
- 緩衝區結構體由兩部分組成:用於緩衝區管理的緩衝頭部分和用於資料塊的資料部分。為了保護核心記憶體,狀態欄位可以定義為一個位向量,其中每個位表示一個唯一的狀態條件。
- 裝置表:每個塊裝置用一個裝置表結構表示。
- 緩衝區初始化:當系統啟動時,所有I/O緩衝區都在空閒列表中,所有裝置列表和T/O佇列均為空。
- 緩衝區列表:當緩衝區分配給(dev,blk)時,它會被插入裝置表的dev_list中。如果緩衝區當前正在使用,則會將其標記為BUSY(繁忙)並從空閒列表中刪除。
- Unix演算法的一些具體說明:
(1 )資料一致性:為確保資料一致性,getblk一定不能給同一個(dev, blk)分配多個緩衝區 這可以通過讓程序從休眠狀態喚醒後再次執行“重試迴圈”來實現。讀者可以驗證 分配的每個緩衝區都是唯一的一其次,髒緩衝區在重新分配之前被寫出來,這保證了資料的一致性。
(2)快取效果:快取效果可通過以下方法實現 釋放的緩衝區保留在裝置列表中,以便 可能重用,標記為延遲寫入的緩衝區不會立即產生I/O,並且可以重用。緩衝區會被釋放到 空閒列表的末尾,但分配是從空閒列表的前面開始的,這是基於LRU (最近最少使用)原則,它有助於延長所分配緩衝區的使用期,從而提高它們的快取效果,
(3)臨界區:裝置中斷處理程式可操作緩衝區列表,例如從裝置表的I/O佇列中刪除 bp,更改其狀態並呼叫brelse(bp)。所以,在getb汰和brelse中,裝置中斷在這些臨界區中 會被遮蔽。這些都是隱含的,但沒有在演算法中表現出來。
- PV演算法:
BUFFER *getb1k(dev,blk): while(1){ (1). P(free); //get a free buffer first if (bp in dev_1ist){ (2). if (bp not BUSY){ remove bp from freelist;P(bp); // lock bp but does not wait (3).return bp; // bp in cache but BUSY V(free); // give up the free buffer (4).P(bp); // wait in bp queue return bp;v // bp not in cache,try to create a bp=(dev,blk) (5).bp = frist buffer taken out of freelist;P(bp); // lock bp,no wait (6).if(bp dirty){ awzite(bp); // write bp out ASYNC,no wait continue; // continue from (1) (7).reassign bp to(dev,blk);1/ mark bp data invalid,not dir return bp;- // end of while(1); brelse(BUFFER *bp), { (8).iF (bp queue has waiter)( V(bp); return; ] (9).if(bp dirty && free queue has waiter){ awrite(bp);zeturn;}(10).enter bp into(tail of) freelist;V(bp);V(free); }
- Unix演算法的缺點:
(1)效率低下:該演算法依賴於重試迴圈,例如,釋放緩衝區可能會喚醒兩組程序:需要釋放的緩衝區的程序,以及只需要空閒緩衝區的程序。由於只有一個程序可以獲取釋放的緩 衝區,所以,其他所有被喚醒的程序必須重新進入休眠狀態。從休眠狀態喚醒後,每個被喚 醒的程序必須從頭開始重新執行演算法,因為所需的緩衝區可能已經存在。這會導致過多的進 程切換。
(2)快取效果不可預知:在Unix演算法中,每個釋放的緩衝區都可被獲取'如果緩衝區 由需要空閒緩衝區的程序獲取,那麼將會重新分配緩衝區,即使有些程序仍然需要當前的緩衝區。
(3)可能會出現飢餓:Unix演算法基於“自由經濟”原則,即每個程序都有嘗試的機會,但不能保證成功,因此,可能會出現程序飢餓
(4)該演算法使用只適用丁單處理器系統的休眠/喚醒操作