postgres源码解析52 磁盘管理器--1

news/2024/4/29 9:22:12/文章来源:https://blog.csdn.net/qq_52668274/article/details/129409079

简介

postgres中的磁盘管理器SMGR对外提供了管理磁盘介质的接口,其主要实现在md.c文件中。磁盘管理器并非对磁盘上的文件直接进行操作,而是通过VFD机制进行文件操作。凡是对存储在磁盘中的表进行访问操作均会与磁盘管理器打交道,由它进行统一处理。

VFD机制

和Linux系统一样,访问每个文件时都会为之分配一个文件描述符fd。但是Linux系统所提供的文件描述符数量是有限的,对于数据库这种频繁访问数据文件的软件而言,很容易超过操作系统对文件描述符数量的支持。针对这一问题,postgres借鉴计算机中的“虚拟技术”扩充文件描述符,即虚拟文件描述符机制VFD,实现对文件的管理机制。

1 实现原理

VFD机制的实现原理并不复杂,当进程在打开一个文件时,总是能够返回一个虚拟的文件描述符,本质是对物理文件操作的进一步封装.
所谓虚拟文件描述符,是指一个VFD的数据结构,其中记录了操作系统为文件分配的真实文件描述符。实际上,一个进程能打开的最大文件数目仍为系统规定的最大值,但是进程使用了VFD机制,导致其自身觉得可以打开任意多的文件。如果多个进程同时对同一个文件操作,那么每个进程均会获得一个VFD,这些VFD所对应的真实文件描述符是同一个,如下如所示。
在这里插入图片描述

2 VFD数据结构

vfd结构体为虚拟文件描述符,记录了真实文件描述符及其状态,下一个空闲VFD所在空闲链表位置和文件名等信息。

typedef struct vfd
{int			fd;				/* current FD, or VFD_CLOSED if none */ unsigned short fdstate;		/* bitflags for VFD's state */ResourceOwner resowner;		/* owner, for automatic cleanup */File		nextFree;		/* link to next free VFD, if in freelist */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) */
} Vfd;/* these are the assigned bits in fdstate below: */
#define FD_DELETE_AT_CLOSE	(1 << 0)	/* T = delete when closed */
#define FD_CLOSE_AT_EOXACT	(1 << 1)	/* T = close at eoXact */
#define FD_TEMP_FILE_LIMIT	(1 << 2)	/* T = respect temp_file_limit */

VfdCache数组维护了进程所持有虚拟文件描述符信息。其中File类型字段为该数组的索引下标,VfdCache[0]仅做为头标识,无实质意义。

/** Virtual File Descriptor array pointer and size.  This grows as* needed.  'File' values are indexes into this array.* Note that VfdCache[0] is not a usable VFD, just a list header.*/
static Vfd *VfdCache;
static Size SizeVfdCache = 0;
3私有函数
APIdescription
Deletedelete a file from the Lru ring
LruDeleteremove a file from the Lru ring and close its FD
Insertput a file at the front of the Lru ring
LruInsertput a file at the front of the Lru ring and open it
ReleaseLruFileRelease an fd by closing the last entry in the Lru ring
ReleaseLruFilesRelease fd(s) until we’re under the max_safe_fds limit
AllocateVfdgrab a free (or new) file record (from VfdCache)
FreeVfdfree a file record
4 VFD具体实现

在这里插入图片描述
如上图,LRU中第一个充当头信息,无实质意义,lruLessRecently指针指向访问频率逐渐降低的VFD,lruMoreRecently指针指向访问频率逐渐增加的VFD。

4.1 LruDelete(File file) : 该函数的功能是从VFD 缓冲池中删除指定的VFD。
1 首先根据 file从VFDCache数组中找到其对应的VFD结构体vfdP;
2 调用系统函数close关闭物理文件,将vfdP->fd置为VFD_CLOSED;
3 递减进程私有的全局VFD计数器;
4 最后调用 Delete函数进行真正的删除操作。
Delete函数比较简单,即更新LRU双向链表的前后指针域。

	vfdP = &VfdCache[file];VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;
static void
LruDelete(File file)
{Vfd		   *vfdP;Assert(file != 0);DO_DB(elog(LOG, "LruDelete %d (%s)",file, VfdCache[file].fileName));vfdP = &VfdCache[file];/** Close the file.  We aren't expecting this to fail; if it does, better* to leak the FD than to mess up our internal state.*/if (close(vfdP->fd) != 0)elog(vfdP->fdstate & FD_TEMP_FILE_LIMIT ? LOG : data_sync_elevel(LOG),"could not close file \"%s\": %m", vfdP->fileName);vfdP->fd = VFD_CLOSED;--nfile;/* delete the vfd record from the LRU ring */Delete(file);
}

4.2 LruInsert(File file) :该函数的功能是将指定的VFD插入LRU缓冲池头部。
1 首先找到 file对应的VFD在VfdCache中的位置;
2 如果文件未打开,先进行安全性检查,确保此时的文件描述符个数不得超过系统所支持的最大安全数目;
3 调用BasicOpenFilePerm打开文件,递增进程私有的全局VFD计数器;
4 最后调用Insert函数进行真正的插入操作;
Insert函数相比于Delete稍复杂,具体操作是将指定的VFD插入VfdCache[0]与VfdCache[0].lruLessRecently之间。

	vfdP = &VfdCache[file];vfdP->lruMoreRecently = 0;vfdP->lruLessRecently = VfdCache[0].lruLessRecently;VfdCache[0].lruLessRecently = file;VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;
/* returns 0 on success, -1 on re-open failure (with errno set) */
static int
LruInsert(File file)
{Vfd		   *vfdP;Assert(file != 0);DO_DB(elog(LOG, "LruInsert %d (%s)",file, VfdCache[file].fileName));vfdP = &VfdCache[file];if (FileIsNotOpen(file)){/* Close excess kernel FDs. */ReleaseLruFiles();/** The open could still fail for lack of file descriptors, eg due to* overall system file table being full.  So, be prepared to release* another FD if necessary...*/vfdP->fd = BasicOpenFilePerm(vfdP->fileName, vfdP->fileFlags,vfdP->fileMode);if (vfdP->fd < 0){DO_DB(elog(LOG, "re-open failed: %m"));return -1;}else{++nfile;}}/** put it at the head of the Lru ring*/Insert(file);return 0;
}

4.3 ReleaseLruFiles:该函数的功能是从VFD 缓冲池中删除最近最不经常使用的VFD,只有在已分配的文件描述符超过系统所能支持的最大文件描述符个数后才会触发后续的操作。

/** Release one kernel FD by closing the least-recently-used VFD.*/
static bool
ReleaseLruFile(void)
{DO_DB(elog(LOG, "ReleaseLruFile. Opened %d", nfile));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);LruDelete(VfdCache[0].lruMoreRecently);return true;			/* freed a file */}return false;				/* no files available to free */
}/** 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.*/
static void
ReleaseLruFiles(void)
{while (nfile + numAllocatedDescs + numExternalFDs >= max_safe_fds){if (!ReleaseLruFile())break;}
}

4.4 AllocateVfd:该函数的功能是从空闲链表中获取下一个可用的VFD,根据具体情况会适当扩大空闲链表。也就是说总会获得一个VFD结构体,除非出现OOM。
1 如果空闲链表已满,则会以2倍方式扩大空闲链表,并初始化空闲链表中的VFD元素。
2 将VfdCache[0].nextFree设置为下一个可用的VFD在LRU缓冲池中的槽索引。
3 更新VfdCache[0].nextFree,即 VfdCache[0].nextFree = VfdCache[file].nextFree;

static File
AllocateVfd(void)
{Index		i;File		file;DO_DB(elog(LOG, "AllocateVfd. Size %zu", SizeVfdCache));Assert(SizeVfdCache > 0);	/* InitFileAccess not called? */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;Vfd		   *newVfdCache;if (newCacheSize < 32)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.*/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;}file = VfdCache[0].nextFree;VfdCache[0].nextFree = VfdCache[file].nextFree;return file;
}

4.5 FreeVfd(File file):该函数的功能释放指定 file 对应的VFD,并将其置于空闲链表头部。

static void
FreeVfd(File file)
{Vfd		   *vfdP = &VfdCache[file];DO_DB(elog(LOG, "FreeVfd: %d (%s)",file, vfdP->fileName ? vfdP->fileName : ""));if (vfdP->fileName != NULL){free(vfdP->fileName);vfdP->fileName = NULL;}vfdP->fdstate = 0x0;vfdP->nextFree = VfdCache[0].nextFree;VfdCache[0].nextFree = file;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_268594.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Gateway学习

文章大纲 为什么需要网关&#xff1f; 传统的单体架构只有一个服务开放给客户端调用&#xff0c;但是在微服务架构体系中是将一个系统拆分成多个微服务&#xff0c;那么作为客户端如何去调用这些微服务呢&#xff1f;如果没有网关的存在&#xff0c;就只能在本地记录每个微服务…

Buuctf [GUET-CTF2019]number_game 题解

目录 一.主函数逻辑 二.level_stor()函数 三.mid_stor函数 四.operate函数 五.judge2函数 六.求解flag 一.主函数逻辑 ①先输入一个字符串,然后judge1()函数遍历它,判断字符是否在[0,4]区间范围内 ②将输入的字符串用层次遍历的方式存储为一个二叉树root ③再将二叉树r…

React解决样式冲突问题的方法

React解决样式冲突问题的方法 前言&#xff1a; 1、React最终编译打包后都在一个html页面中&#xff0c;如果在两个组件中取一样类名分别引用在自身&#xff0c;那么后者会覆盖前者。 2、默认情况下&#xff0c;只要导入了组件&#xff0c;不管组件有没有显示在页面中&#x…

科技成果赋智中小企业深度行 边界无限靖云甲ADR入选十大优秀案例

近日&#xff0c;国家工业信息安全发展研究中心、青岛市工业和信息化局、青岛市民营经济发展局、青岛市即墨区人民政府、青岛蓝谷管理局联合举办的科技成果赋智中小企业“深度行”活动&#xff08;青岛站&#xff09;成功举办&#xff0c;同步举行了赋智“深度行”活动&#xf…

打怪升级之发送单个UDP包升级版

目标 1.message的输入由edit_control进行&#xff0c;需要捕获输入。 2.用户的主机地址和发送地址不一样&#xff0c;需要分别设置并绑定。 设计RC外观 必备组件&#xff1a;主机IP与端口&#xff0c;从机IP与端口&#xff0c;消息框&#xff0c;发送&#xff0c;连接按钮。…

KDHG-A变频互感器综合测试仪

一、概述 KDHG-A电流互感器现场综合测试仪是一种专门为测试互感器&#xff1a;伏安特性、变比、极性、误差曲线、计算拐点和二次侧回路检查等设计的多功能现场试验仪器。 二、主要特点 1&#xff0e;单机220V输入时最大电压输出0-2500V&#xff0c;单机最大电流输出0-1000A&am…

k8s--pod管理-资源清单-生命周期

文章目录一、资源清单1. 格式和内容的书写方法2. 示例及执行操作二、pod生命周期:Init容器&探针1.简介2.Init 容器3.探针3.1存活探针3.2就绪探针一、资源清单 - 格式如下&#xff1a;apiVersion: group/version  //指明api资源属于哪个群组和版本&#xff0c;同一个组可…

金三银四面试热潮将至,靠这一份软件测试面经,offer拿到手软

不知不觉又到了新一年的金三银四&#xff0c; 去年的疫情紧张&#xff0c;造成的一系列影响我相信大家都还历历在目&#xff0c;尤其是工作这块更是如此&#xff0c;找工作的紧迫度&#xff0c;导致很大部分人群在工作发展可能并没有想象中的那样迅速。 作为一名在职的测试人员…

git团队合作 - branch分支的使用、主分支合并、冲突处理方案

情景例子开发部3人&#xff0c;组长man&#xff0c; 组员devA&#xff0c;devB&#xff1b;1&#xff09;组长man负责代码合并、冲突处理、检查代码、合并代码到master主分支&#xff1b;2&#xff09;组员devA负责开发3&#xff09;组员devB负责开发git仓库主次分支安排1&…

C++ linux下获取时间戳 秒、微妙、纳秒

1.例子#include <iostream>#include <sys/time.h>#include <cstdlib>#include <cstdio>#include <ctime>#include <cmath>#include <unistd.h>usingnamespace std;time_t clocktime(){time_t t time(NULL);std::cout << &quo…

swoole的强大之处,你可能只是略知一二!

首先 swoole 是 php 的一个扩展程序swoole 是一个为 php 用 c 和 c 编写的基于事件的高性能异步 & 协程并行网络通信引擎swoole 是一个多进程模型的框架&#xff0c;当启动一个进程 swoole 应用时&#xff0c;一共会创建 2nm 个进程&#xff0c;n 为 worker 进程数&#xf…

vector你得知道的知识

vector的基本使用和模拟实现 一、std::vector基本介绍 1.1 常用接口说明 std::vector是STL中的一个动态数组容器&#xff0c;它可以自动调整大小&#xff0c;支持在数组末尾快速添加和删除元素&#xff0c;还支持随机访问元素。 以下是std::vector常用的接口及其说明&#xf…

熬夜30天吃透这九大Java核心专题,我收割了3个大厂offer

这次一共收割了3个大厂offer&#xff0c;分别是蚂蚁金服、美团和网易&#xff0c;特意分享这次对我帮助非常大的宝典资料&#xff0c;一共涉及九大核心专题&#xff0c;分别是计算机网络、操作系统、MySQL、Linux、JAVA、JVM、Redis、消息队列与分布式、网站优化相关&#xff0…

DSF深度搜索时到底是如何回溯的(小tip)

这一段让我迷了两次&#xff0c;为什么回溯的时候&#xff0c;恢复了最后一位&#xff0c;往上递归一层之后&#xff0c;把最后一位填在它前一位&#xff0c;但是原本的前一位没有恢复&#xff0c;最后一位要怎么办&#xff1f;其实这还是递归没明白 也就是这一步是如何实现的 …

一点就分享系列(实践篇6——上篇)【迟到补发】Yolo-High_level系列算法开源项目融入V8 旨在研究和兼容使用【持续更新】

一点就分享系列&#xff08;实践篇5-补更篇&#xff09;[迟到补发]—Yolo系列算法开源项目融入V8旨在研究和兼容使用[持续更新] 题外话 去年我一直复读机式强调High-level在工业界已经饱和的情况&#xff0c;目的是呼吁更多人看准自己&#xff0c;不管是数字孪生交叉领域&#…

C++基础了解-21-C++ 继承

C 继承 一、C 继承 面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类&#xff0c;这使得创建和维护一个应用程序变得更容易。这样做&#xff0c;也达到了重用代码功能和提高执行效率的效果。 当创建一个类时&#xff0c;不需要重新编写新的…

量子计算(8)pyqpanda编程3测量操作

作为一名高产博主&#xff0c;小编我一天不写文章就浑身难受&#xff0c;这不&#xff0c;一闲下来就来给大家科普量子计算编程操作了。 今天我们要来探讨“测量操作”&#xff0c;众所周知&#xff0c;薛定谔的猫是一种既死又活的状态&#xff0c;很多人认为&#xff0c;猫是死…

Java代码优化|提高代码质量的一些小技巧

1.需要 Map 的主键和取值时&#xff0c;应该迭代 entrySet()当循环中只需要 Map 的主键时&#xff0c;迭代 keySet() 是正确的。但是&#xff0c;当需要主键和取值时&#xff0c;迭代 entrySet() 才是更高效的做法&#xff0c;比先迭代 keySet() 后再去 get 取值性能更佳。正例…

Git设置SSH Key

一、git 配置 &#xff08;1&#xff09;打开 git 命令窗口 &#xff08;2&#xff09;配置用户名&#xff08;填自己的姓名&#xff09; git config --global user.name “xinyu.xia” &#xff08;3&#xff09;配置用户邮箱&#xff08;填自己的邮箱&#xff0…

Python+Yolov8目标识别特征检测

Yolov8目标识别特征检测如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<Yolov8目标识别特征检测>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推荐…