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 |
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){
SizenewCacheSize = 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;
- 半自动化运维之动态添加数据文件(二) (r5笔记第56天)
- 11g Active DataGuard初探(r5笔记第54天)
- Github 项目推荐 | 用于构建端对端对话系统和训练聊天机器人的开源库 —— DeepPavlov
- 我身边的一些数据库事故 (r5笔记第52天)
- 一个清理脚本的改进思路(r5笔记第51天)
- 【专业技术】Python爬虫:抓取手机APP的传输数据
- 海量数据迁移之传输表空间(一) (r5笔记第71天)
- 一条sql语句的改进探索(r5笔记第70天)
- 【专业技术】Node.js 究竟是什么?
- Github 项目推荐 | 用 Pytorch 实现的 WaveNet-Vocoder
- 重启数据库的一场闹剧(r5笔记第68天)
- 【C语言系列】基础语法案例分析(初级篇)
- 一次ORA-00600问题的排查和分析(r5笔记第64、65天)
- SpringMVC入门就这么简单
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- PHP的PDO连接讲解
- PHP生成指定范围内的N个不重复的随机数
- 实例讲解通过PHP创建数据库
- PHP的mysqli_select_db()函数讲解
- PHP的PDO事务与自动提交
- 使用pytorch实现论文中的unet网络
- Python如何优雅删除字符列表空字符及None元素
- php语法检查的方法总结
- PHP实现浏览器格式化显示XML的方法示例
- Laravel框架基于中间件实现禁止未登录用户访问页面功能示例
- PHP的mysqli_stmt_init()函数讲解
- PHP内置函数生成随机数实例
- PHPStudy下如何为Apache安装SSL证书的方法步骤
- PHP的mysqli_thread_id()函数讲解
- thinkPHP框架中layer.js的封装与使用方法示例