PostgreSQL VFD機制

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

VFDfree鏈表,實際上是數組的下標。

lruMoreRecently

VFD的最近最少使用鏈表,爲雙向。實際上也是數組的下標

lruLe***ecently

lruLe***ecently爲正向,每次插入都插入頭部

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時的流程

1Open時首先會調用AllocateVfd,從VfdCache數組中找一個空閒的slot,然後返回。該函數流程見AllocateVfd調用。

2)然後會調用ReleaseLruFiles判斷是否open了最大限制的fd。如超出限制,則將LRU鏈表最後一個VFDfd close掉。

3open文件,並將該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文件前都會調用AllocateVfdVfdCache中獲取一個空閒的vfd

2)首先會判斷free鏈表中是否爲空。初始時刻,SizeVfdCache1,則會將VfdCache初始化成大小32的數組,並將其通過nextFree串聯起來形成free鏈表,注意該free鏈表爲循環。

3VfdCache[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;

image.png

ReleaseLruFiles

1nfileopen打開的文件數,numAllocatedDescsfopen打開的文件數,max_safe_fds爲操作系統計算得出的值。

2)一旦超出max_safe_fds值,就會調用ReleaseLruFileLRU鏈表刪除一個,注意刪除的是VfdCache[0].lruMoreRecently,即鏈表的尾部,最近最少使用的。

3首先將該fd關閉,然後將之置爲VFD_CLOSED。調用Delete函數將VFDLRU鏈表刪除。注意這裏只是從LRU鏈表刪除,不會釋放回收到free鏈表,也不會修改vfd數據結構的其他成員變量值。因爲後續可能還會用到該物理文件,會重新open並將之重新insertLRU鏈表。

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->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently;
            VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;

3、Insert

Insert->
    vfdP = &VfdCache[file];
    vfdP->lruMoreRecently = 0;
    vfdP->lruLe***ecently = VfdCache[0].lruLe***ecently;
    VfdCache[0].lruLe***ecently = file;
    VfdCache[vfdP->lruLe***ecently].lruMoreRecently = file;

 

LRU鏈表的形式如下:

image.png

Insert一個VFD時:

image.png

4、Delete

Delete(file);-->
    vfdP = &VfdCache[file];
    VfdCache[vfdP->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently;
    VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;

例如刪除VfdCache[1]

image.png

5、回收VFD

1)每次調用FileClose時,會回收vfdfree鏈表。

2)先調用close函數

3)然後將之從LRU鏈表刪除

4)如果是臨時文件,還會將臨時文件刪除

5)調用FreeVfdvfd回收到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;


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章