瀚高数据库
目录
环境
文档用途
详细信息
环境
系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:14
文档用途
了解VFD
详细信息
1.相关数据类型
typedef struct vfd
{
int fd; /* current FD, or VFD_CLOSED if none */ OS文件描述符
unsigned short fdstate; /* bitflags for VFD's state */ vfd状态
ResourceOwner resowner; /* owner, for automatic cleanup */ 拥有者,自动清理用
File nextFree; /* link to next free VFD, if in freelist */ File为int类型,表示下标
File lruMoreRecently; /* doubly linked recency-of-use list */
File lruLessRecently;
off_t fileSize; /* current size of file (0 if not temporary) */
char *fileName; /* name of file, or NULL for unused VFD */
/* NB: fileName is malloc'd, and must be free'd when closing the VFD */
int fileFlags; /* open(2) flags for (re)opening the file */
mode_t fileMode; /* mode to pass to open(2) */ 读、 写、 执行等flag
} Vfd;
2. VfdCache初始化
注意:VfdCache[0]不是一个有效的vfd,仅仅是为了减少链表增加、删除时的判断而引入的头结点。
Assert(SizeVfdCache == 0); /* call me only once */
/* initialize cache header entry */
VfdCache = (Vfd *) malloc(sizeof(Vfd));
MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
VfdCache->fd = VFD_CLOSED; // 全局的VfdCache指向新建的表头节点
SizeVfdCache = 1; // 记录vfd的数量
初始化完成之后,VfdCache指向内存空间的某一块:
3.AllocateVfd
Assert(SizeVfdCache > 0); /* InitFileAccess not called? */
// 空闲链表上没有可用的vfd
if (VfdCache[0].nextFree == 0)
{
/*
* The free list is empty so it is time to increase the size of the
* array. We choose to double it each time this happens. However,
* there's not much point in starting *real* small.
*/
Size newCacheSize = SizeVfdCache * 2; // 原有容量*2
Vfd *newVfdCache;
if (newCacheSize < 32) // 32个起步,考虑初始化就直接分配,这样的话vfd只有头结点,算作一个,这样分配才2个,太少了。
newCacheSize = 32;
/*
* Be careful not to clobber VfdCache ptr if realloc fails.
*/
newVfdCache = (Vfd *) realloc(VfdCache, sizeof(Vfd) * newCacheSize);
if (newVfdCache == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
VfdCache = newVfdCache; // 新内存片的起始地址
/*
* Initialize the new entries and link them into the free list.
*/
// 之前的vfd已经通过realloc拷贝,只需初始化刚刚分配的vfd,从SizeVfdCache开始,一直到末尾
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;
/*
* Record the new size
*/
SizeVfdCache = newCacheSize; // SizeVfdCache记录现在的vfd数组大小
}
// 返回下标并修改指向下一个free节点的指针
file = VfdCache[0].nextFree;
VfdCache[0].nextFree = VfdCache[file].nextFree;
return file;
}
如图所示(依据环境为vfd初始化后第一次分配的情况):
4.释放内核维护的文件描述符
注意:vfd结构本身没有被释放,因为分配的时候是连续分配。只是更改了其中的fd为VFD_CLOSED,并对计数-1。
1.判断是否超过了max_safe_fds,值为一个固定的数字,pg14是48
/*
* Release kernel FDs as needed to get under the max_safe_fds limit.
* After calling this, it's OK to try to open another file.
*/
// nfile是经由fd.c通过open打开的, numAllocatedDescs是经由fd.c通过fopen打开的, numExternalFDs是系统或者没有通过fd.c打开的文件描述符
static void
ReleaseLruFiles(void)
{
while (nfile + numAllocatedDescs + numExternalFDs >= max_safe_fds)
{
if (!ReleaseLruFile())
break;
}
}
2.nfile > 0 表示当前有打开的文件,关闭掉最近最少使用的文件描述符(就是最早打开的那个)。
/*
* Release one kernel FD by closing the least-recently-used VFD.
*/
// 最近最少使用的fd关闭掉,直接关闭掉第一个节点
static bool
ReleaseLruFile(void)
{
if (nfile > 0)
{
/*
* There are opened files and so there should be at least one used vfd
* in the ring.
*/
Assert(VfdCache[0].lruMoreRecently != 0); // 最早打开的文件描述符在表尾,通过VfdCache[0].lruMoreRecently索引
LruDelete(VfdCache[0].lruMoreRecently);
return true; /* freed a file */
}
return false; /* no files available to free */
}
3.调用close关闭文件描述符,修改vfd结构相关成员变量。
static void
LruDelete(File file)
{
Vfd *vfdP;
Assert(file != 0); // 不删头结点
vfdP = &VfdCache[file];
close(vfdP->fd); // 关闭系统文件描述符
vfdP->fd = VFD_CLOSED; // fd设置为无效状态
--nfile; // 计数减一
/* delete the vfd record from the LRU ring */
Delete(file);
}
4.调整VfdCache指向的数组结构
static void
Delete(File file)
{
Vfd *vfdP;
Assert(file != 0);
vfdP = &VfdCache[file];
VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;
VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;
}
如图所示:
- 给定文件路径,打开文件, 获取kernel fd
/*
* Open a file with BasicOpenFilePerm() and pass default file mode for the
* fileMode parameter.
*/
// fileFlags : 文件读、写、执行、截断、创建等
// int pg_file_create_mode = PG_FILE_MODE_OWNER = S_IRUSR | S_IWUSR , 用户读写,受到进程mask的影响
int
BasicOpenFile(const char *fileName, int fileFlags)
{
return BasicOpenFilePerm(fileName, fileFlags, pg_file_create_mode);
}
进入到BasicOpenFilePerm:
int
BasicOpenFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
{
int fd;
tryAgain:
// pg自定义PG_O_DIRECT不要和系统存在的flag相冲突
// PG_O_DIRECT_USE_F_NOCACHE这个宏主要用于做系统兼容,macOS中open()系统调用没有O_DIRECT,所以只能用fcntl()去修改File status flags,然而大部分类unix系统都可以在open时指定O_DIRECT。
//指定O_DIRECT会在文件读写时绕过操作系统缓存,这个标志只是建议,不一定有效。
#ifdef PG_O_DIRECT_USE_F_NOCACHE
/*
* The value we defined to stand in for O_DIRECT when simulating it with
* F_NOCACHE had better not collide with any of the standard flags.
*/
StaticAssertStmt((PG_O_DIRECT &
(O_APPEND |
O_CREAT |
O_EXCL |
O_RDWR |
O_RDONLY |
O_SYNC |
O_TRUNC |
O_WRONLY)) == 0,
"PG_O_DIRECT value collides with standard flag");
#if defined(O_CLOEXEC)
StaticAssertStmt((PG_O_DIRECT & O_CLOEXEC) == 0,
"PG_O_DIRECT value collides with O_CLOEXEC");
#endif
#if defined(O_DSYNC)
StaticAssertStmt((PG_O_DIRECT & O_DSYNC) == 0,
"PG_O_DIRECT value collides with O_DSYNC");
#endif
//没有O_DIRECT标志,只能用fcntl在open之后获取到fd之后再修改,所以这个表达式把PG_O_DIRECT拿掉,fileFlags本身没有变化
fd = open(fileName, fileFlags & ~PG_O_DIRECT, fileMode);
#else
fd = open(fileName, fileFlags, fileMode);
#endif
if (fd >= 0)
{
#ifdef PG_O_DIRECT_USE_F_NOCACHE
if (fileFlags & PG_O_DIRECT)
{
if (fcntl(fd, F_NOCACHE, 1) < 0)
{
// errno是全局的,close()也会设置errno,所以这里在进入close之前先保存一下
int save_errno = errno;
close(fd);
errno = save_errno;
return -1;
}
}
#endif
return fd; /* success! */
}
// open调用失败,设置errno,判断errno的值:
// EMFILE:每个进程能打开的文件描述符有限制,意味着超过了限制;
// ENFILE:超出了当前系统文件描述符的限制,统计了所有进程打开的
// 当是上面两种错误时,利用lru算法,关闭最近最少使用的文件描述符,重新再打开一次,如果不是,返回-1表示失败。
if (errno == EMFILE || errno == ENFILE)
{
int save_errno = errno;
ereport(LOG,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("out of file descriptors: %m; release and retry")));
errno = 0;
if (ReleaseLruFile())
goto tryAgain;
errno = save_errno;
}
return -1; /* failure */
}
6.FreeVfd
fileName要释放,它是单独申请的一块内存
把该vfd挂到free列表上,这块空间没法free
static void
FreeVfd(File file)
{
Vfd *vfdP = &VfdCache[file];
if (vfdP->fileName != NULL)
{
free(vfdP->fileName);
vfdP->fileName = NULL;
}
vfdP->fdstate = 0x0;
vfdP->nextFree = VfdCache[0].nextFree;
VfdCache[0].nextFree = file;
}
7.Insert
static void
Insert(File file)
{
Vfd *vfdP;
Assert(file != 0);
vfdP = &VfdCache[file];
vfdP->lruMoreRecently = 0;
vfdP->lruLessRecently = VfdCache[0].lruLessRecently;
VfdCache[0].lruLessRecently = file;
VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;
}