1. 程式人生 > 其它 >十一週學習筆記

十一週學習筆記

塊裝置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 for
I/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)該演算法使用只適用丁單處理器系統的休眠/喚醒操作