PostgreSQL 中的虛擬文件描述符

由于每個操作系統(tǒng)限制了一個進程能打開的文件數(shù)(例如:ubuntu 為1024),因此進程能獲得的文件描述符是有限的。對于經(jīng)常需要打開許多文件的數(shù)據(jù)庫進程來說,很容易會超過操作系統(tǒng)對于文件描述符數(shù)量的限制。
為解決這個問題,PostgreSQL 中使用了虛擬文件描述符?(VFD)?機制,通過 VFD 管理真實的文件描述符,幫助進程擺脫操作系統(tǒng)的限制。
VDF 中各字段含義:
fd 記錄該 VFD 所對應(yīng)的真實文件描述符,如果當前 VFD 沒有打開文件,則其值為 VFD_CLOSED (-1)。
fdstate 標記位:如 FD_DELETE_AT_CLOSE,表明該文件在關(guān)閉時要被刪除
nextFree:指向下一個空閑的 VFD,其數(shù)據(jù)類型 File 表示其在 VFD 數(shù)組中的下標。
lruMoreRecently:指向比該 VFD 最近更常用的虛擬文件描述符。
lnuLessRecently:指向比該 VFD 最近更不常用的虛擬文件描述符。
fileSize:當前文件大小。
fleName:該 VFD 對應(yīng)文件的文件名,如果是空閑的 VFD 則 fileName 為空。
fileFlags:該文件打開時的標志,包括只讀、只寫、讀寫等。
fileMode:文件創(chuàng)建時所指定的模式。
VFD 數(shù)組 VfdCache 作為 LRU 池管理文件描述符,并根據(jù)需要打開和關(guān)閉實際的 OS 文件描述符。LRU 池使用數(shù)組實現(xiàn),數(shù)組元素是 VFD 結(jié)構(gòu)體,數(shù)組大小會根據(jù)需要增長(最大1024)。

當 LRU 池未滿時,進程可以申請一個 VFD 用來打開物理文件;而當 LRU 池已滿時,進程需要首先關(guān)閉一個 VFD。在 LRU 中,使用替換最長時間未使用 VFD 策略。
進程在 VfdCache 上保持了兩個鏈表,一個是 LRU 池(雙向鏈表),另一個是 FreeList (空閑鏈表,記錄了所有可被分配的 VFD)。前者通過 lruMoreRecently 屬性和 lruLessRecently 屬性來鏈接,后者則通過 nextFree 屬性來鏈接。
VfdCache [0] 不是可用的 VFD,它僅用來標識 FreeList 和 LRU 池的鏈表頭部。對 LRU 池里的 VFD 的操作主要包括以下三種:
(1)?將 VFD 插入?LRU?池
打開一個新的 VFD 時,會將該 VFD 對應(yīng)的物理文件打開,并將該 VFD 插入到 VfdCache[O] 之后的位置。例如,我們要插入的 VFD 為 a,首先要根據(jù) a 中的 fd 字段判斷對應(yīng)的物理文件是否已經(jīng)打開。如果沒有打開則根據(jù) a 中的 fileName 打開此文件,并插入到 LRU 池中。
插入時要將 a 的 lruMoreRecently 指向 VfdCache[0],VfdCache[0] 的 lruLessRecently 指向 a,然后 a 的 lruLessRecently 指向 V1,V1?的 lruMoreRecently 指向 a。
(2)?從?LRU?池刪除?VFD
進程使用完一個文件并關(guān)閉它時,將該文件的 VFD 從 LRU 池中刪除,并將該 VFD 對應(yīng)的文件關(guān)閉掉。例如,我們要對 V2?操作,則首先將?V1?的 lrulessRecently 指向 V3,將 V3?的 lruMoreRecently 指向 V1。?
這樣就將 V2?從 LRU 池中刪除了,如果V2?的fdstate 被置為 FD_DELETE_AT_CLOSE,則要先將V2對應(yīng)的文件刪除,再清掉FD_DELETE_AT_CLOSE 位,接著將 fileSize 置為 0,將 fd 置為 VFD_CLOSED。這樣?V2?就變?yōu)榭臻e,最后將其加入到空閑鏈表中。
(3)?刪除?LRU?池尾?VFD
當 LRU 池已滿,而此時又要打開新的文件時,就需將池尾的 VFD 刪掉,這樣新打開的 VFD 就可以插入到 LRU 中。注意,這里被刪除的 VFD 僅僅只是從 LRU 池中脫鏈并關(guān)閉其對應(yīng)的物理文件,VFD 本身并不做其他修改和刪除。