VFD機制中由結構體struct vfd來維護。其中各個成員變量的意義以下表所示:數組
fd數據結構 |
vfd實際對應的物理文件文件描述符ide |
fdstate函數 |
FD_DELETE_AT_CLOSE:表示文件在關閉時需刪除spa FD_TEMP_FILE_LIMIT:標記臨時文件操作系統 FD_CLOSE_AT_EOXACT:進程 這幾個都針對臨時文件內存 |
resownerit |
owner, for automatic cleanuptable |
nextFree |
VFD的free鏈表,其實是數組的下標。 |
lruMoreRecently |
VFD的最近最少使用鏈表,爲雙向。實際上也是數組的下標 |
lruLe***ecently |
lruLe***ecently爲正向,每次插入都插入頭部 |
fileSize |
文件大小 |
fileName |
文件名 |
fileFlags |
打開文件時的標籤,好比O_CREATE等 |
fileMode |
打開文件時的屬性,好比讀寫權限等 |
啓動時初始化,使用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;
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;
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;
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->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently; VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;
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鏈表的形式以下:
Insert一個VFD時:
Delete(file);--> vfdP = &VfdCache[file]; VfdCache[vfdP->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently; VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;
例如刪除VfdCache[1]:
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回收,注意幾個成員變量的修改。回收時,將之插入到free鏈表頭部。注意每次取時也從頭部取
FreeVfd-> free(vfdP->fileName);//注意fileName須要釋放,他是另malloc的 vfdP->fileName = NULL; vfdP->fdstate = 0x0; vfdP->nextFree = VfdCache[0].nextFree; VfdCache[0].nextFree = file;