PostgreSQL VFD機制

yzs87發表於2019-11-04

1、結構體

VFD 機制中由結構體 struct vfd 來維護。其中各個成員變數的意義如下表所示:

fd

vfd 實際對應的物理檔案檔案描述符

fdstate

FD_DELETE_AT_CLOSE :表示檔案在關閉時需刪除

FD_TEMP_FILE_LIMIT :標記臨時檔案

FD_CLOSE_AT_EOXACT

這幾個都針對臨時檔案

resowner

owner, for automatic cleanup

nextFree

VFD free 連結串列,實際上是陣列的下標。

lruMoreRecently

VFD 的最近最少使用連結串列,為雙向。實際上也是陣列的下標

lruLessRecently

lruLessRecently 為正向,每次插入都插入頭部

fileSize

檔案大小

fileName

檔名

fileFlags

開啟檔案時的標籤,比如 O_CREATE

fileMode

開啟檔案時的屬性,比如讀寫許可權等

2、初始化

啟動時初始化,使用malloc ,只在本程式中有效,即每個程式都維護各自的 VfdCache 而並非共享記憶體。初始化時只申請第一個陣列,並將其 fd 置為 VFD_CLOSED

PostgresMain->BaseInit->InitFileAccess:
    VfdCache = (Vfd *) malloc(sizeof(Vfd));
    MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
    VfdCache->fd = VFD_CLOSED;
    SizeVfdCache = 1;

2、open時的流程

1 Open 時首先會呼叫 AllocateVfd ,從 VfdCache 陣列中找一個空閒的 slot ,然後返回。該函式流程見 AllocateVfd 呼叫。

2 )然後會呼叫 ReleaseLruFiles 判斷是否 open 了最大限制的 fd 。如超出限制,則將 LRU 連結串列最後一個 VFD fd close 掉。

3 open 檔案,並將該 VFD 插入到 LRU 連結串列。插入 LRU 的函式 Insert 詳細流程看下面的函式分析。

4 )然後對 vfdP 成員變數進行賦值。

PathNameOpenFilePerm->
    file = AllocateVfd();
    vfdP = &VfdCache[file];
    ReleaseLruFiles();
    vfdP->fd = BasicOpenFilePerm(fileName, fileFlags, fileMode);
    Insert(file);
    vfdP->fileName = fnamecopy;
    /* Saved flags are adjusted to be OK for re-opening file */
    vfdP->fileFlags = fileFlags & ~(O_CREAT | O_TRUNC | O_EXCL);
    vfdP->fileMode = fileMode;
    vfdP->fileSize = 0;
    vfdP->fdstate = 0x0;
    vfdP->resowner = NULL;

AllocateVfd

1 )每次呼叫 BasicOpenFilePerm open 檔案前都會呼叫 AllocateVfd VfdCache 中獲取一個空閒的 vfd

2 )首先會判斷 free 連結串列中是否為空。初始時刻, SizeVfdCache 1 ,則會將 VfdCache 初始化成大小 32 的陣列,並將其通過 nextFree 串聯起來形成 free 連結串列,注意該 free 連結串列為迴圈。

3 VfdCache[0] 不使用。最開始 32 個的時候,即第一次擴充後 free 連結串列如下圖所示,跳過 VfdCache[1] 1 會返回。也就是說每次取 VFD 都是 VfdCache[0].nextFree

4 )後續再次擴充時,都是翻倍進行擴充

AllocateVfd->
    if (VfdCache[0].nextFree == 0){
        Size  newCacheSize = SizeVfdCache * 2;
        if (newCacheSize < 32)
            newCacheSize = 32;
        newVfdCache = (Vfd *) realloc(VfdCache, sizeof(Vfd) * newCacheSize);
        VfdCache = newVfdCache;
        for (i = SizeVfdCache; i < newCacheSize; i++){
            MemSet((char *) &(VfdCache[i]), 0, sizeof(Vfd));
            VfdCache[i].nextFree = i + 1;
            VfdCache[i].fd = VFD_CLOSED;
        }
        VfdCache[newCacheSize - 1].nextFree = 0;
        VfdCache[0].nextFree = SizeVfdCache;
        SizeVfdCache = newCacheSize;
    }
    file = VfdCache[0].nextFree;
    VfdCache[0].nextFree = VfdCache[file].nextFree;
    return file;

ReleaseLruFiles

1 nfile open 開啟的檔案數, numAllocatedDescs fopen 開啟的檔案數, max_safe_fds 為作業系統計算得出的值。

2 )一旦超出 max_safe_fds 值,就會呼叫 ReleaseLruFile LRU 連結串列刪除一個,注意刪除的是 VfdCache[0].lruMoreRecently ,即連結串列的尾部,最近最少使用的。

3 首先將該fd 關閉 ,然後將之置為VFD_CLOSED 。呼叫 Delete 函式將 VFD LRU 連結串列刪除。注意這裡只是從 LRU 連結串列刪除,不會釋放回收到 free 連結串列,也不會修改 vfd 資料結構的其他成員變數值。因為後續可能還會用到該物理檔案,會重新 open 並將之重新 insert LRU 連結串列。

ReleaseLruFiles->
    while (nfile + numAllocatedDescs >= max_safe_fds){
        if (!ReleaseLruFile())
            break;
    }
ReleaseLruFile->
    LruDelete(VfdCache[0].lruMoreRecently);->
        vfdP = &VfdCache[file];
        close(vfdP->fd);
        vfdP->fd = VFD_CLOSED;
        --nfile;
        Delete(file);-->
            vfdP = &VfdCache[file];
            VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;
            VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;

3、Insert

Insert->
    vfdP = &VfdCache[file];
    vfdP->lruMoreRecently = 0;
    vfdP->lruLessRecently = VfdCache[0].lruLessRecently;
    VfdCache[0].lruLessRecently = file;
    VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;

LRU 連結串列的形式如下:

Insert 一個 VFD 時:

4、Delete

Delete(file);-->
    vfdP = &VfdCache[file];
    VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;
    VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;

例如刪除VfdCache[1]

5、回收VFD

1 )每次呼叫 FileClose 時,會回收 vfd free 連結串列。

2 )先呼叫 close 函式

3 )然後將之從 LRU 連結串列刪除

4 )如果是臨時檔案,還會將臨時檔案刪除

5 )呼叫 FreeVfd vfd 回收到 free 連結串列

FileClose->
    close(vfdP->fd);
    --nfile;
    vfdP->fd = VFD_CLOSED;
    Delete(file);
    ...
    FreeVfd(file);

 

FreeVfd

呼叫函式FreeVfd 回收,注意幾個成員變數的修改。回收時,將之插入到 free 連結串列頭部。注意每次取時也從頭部取

FreeVfd->
    free(vfdP->fileName);//注意fileName需要釋放,他是另malloc的
    vfdP->fileName = NULL;
    vfdP->fdstate = 0x0;
    vfdP->nextFree = VfdCache[0].nextFree;
    VfdCache[0].nextFree = file;


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2662625/,如需轉載,請註明出處,否則將追究法律責任。

相關文章