海山数据库(He3DB)技术分享:He3DB Virtual File Descriptor实现原理

“华为杯”研究生数学建模竞赛2020年-【华为杯】D题:无人机集群协同对抗(附获奖论文和MATLAB代码实现) 1.问题重述摘 要:本文针对非线性约束条件下红蓝双方无人机集群协同对抗的最优规划问题,结合贪婪队形、非线性规划、内点法、蒙特卡洛方法和全联立正交配置有限元法,构建了无人机集群协同对抗推演模型。,构建了。我们综合考虑了,和。首先分析红方在集群约束下的贪婪队形和拦截策略,然后我们基于内点法联合MATLAB和AMPL编程求解蓝方突防的非线性规划解,最后基于蒙特卡洛法对整个蓝方突防过程进行推演,得到突防概率图。通过推演,我们提出了两块的区域作为所求区域,并对蓝方的最优突防策略进行分析。 阅读详情

引言

   在 He3DB 这样的数据库系统中,文件操作不仅频繁而且复杂。操作系统提供的文件描述符(FD)数量是有限的,尤其在高并发和大规模数据库操作中,文件描述符资源可能迅速耗尽。为了应对这一挑战,He3DB 引入了 VFD 机制。VFD 通过抽象操作系统的文件描述符,实现了一个虚拟层,使得系统能够有效管理文件资源,同时确保性能的稳定性。

一、VFD (Virtual File Descriptor)的来源

   操作系统的文件描述符是用于表示已经打开的文件的句柄。在 He3DB 中,直接使用操作系统提供的文件描述符存在一些限制,如文件描述符数量的限制,文件频繁打开和关闭带来的性能开销等。为了克服这些问题,He3DB 设计了 VFD 这一抽象层,通过在用户空间内管理文件描述符,减少对操作系统直接资源的依赖,并提供了灵活的文件操作接口。VFD 的设计目标包括:

  • 减少文件描述符的开销:通过 VFD 管理,避免频繁的文件打开和关闭操作。
  • 统一管理文件资源:通过统一的接口和数据结构来管理所有文件资源,方便资源的调度和回收。
  • 优化系统性能:通过 LRU 算法管理VFD,确保系统在资源紧张时优先释放最久未使用的资源。
    从Linux架构来看,VFD位于应用层的系统调用(即open,read等)函数上方。如下图所示:
    在这里插入图片描述

1.1 VFD 的数据结构

   在 He3DB 中,VFD 主要由一个结构体来表示,该结构体定义在 src/backend/storage/file/fd.c 文件中。这个结构体不仅存储了文件的基本信息,还包括管理文件描述符所需的元数据。

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;
  1. fd
    说明: 当前实际的文件描述符。如果没有文件被打开,这个值将被设置为VFD_CLOSED(通常是一个负值,表示文件描述符无效)。
    作用: 用于标识和操作实际打开的文件。fd是与操作系统进行文件操作的关键元素。
  2. fdstate
    说明: 用于存储VFD状态的位标志。
    作用: 通过位标志,可以表示VFD的不同状态,比如是否被占用、是否在使用中等。位标志提供了对VFD状态的详细控制。
  3. resowner
    说明: 表示VFD的资源拥有者。
    作用: 用于自动清理和回收资源。通过资源拥有者,系统可以在特定的资源拥有者(如事务、会话等)结束时自动清理相关的VFD资源。
  4. nextFree
    说明: 链接到下一个空闲的VFD,如果当前VFD在空闲列表中。
    作用: 支持VFD池的管理和复用。空闲的VFD通过链表连接,以便在需要时快速分配。
  5. lruMoreRecently 和 lruLessRecently
    说明: 这两个字段用于双向链表,表示VFD在LRU(Least Recently Used)列表中的位置。
    作用: 用于维护VFD的使用顺序。lruMoreRecently指向最近使用的VFD,lruLessRecently指向较久未使用的VFD。通过双向链表,系统能够快速访问最近使用和最久未使用的VFD,优化VFD的管理和回收。
  6. fileSize
    说明: 当前文件的大小,如果文件不是临时的,则为0。
    作用: 用于存储文件的实际大小。对于临时文件,fileSize为0;对于其他文件,它记录了文件的实际大小,以支持进一步的操作和管理。
  7. fileName
    说明: 文件的名称或路径,如果VFD未使用,则为NULL。
    作用: 用于记录文件的名称。这个字段指向一个动态分配的字符串,存储了文件的路径或名称。在VFD关闭时,必须释放这个内存。
  8. fileFlags
    说明: 文件打开时的标志(如open(2)函数的标志)。
    作用: 用于控制文件的打开模式,如只读、读写等。通过这些标志,系统可以指定文件的打开方式和权限。
  9. fileMode
    说明: 传递给open(2)函数的模式。
    作用: 用于指定文件的创建模式。它定义了文件的权限(如读、写)以及其他文件属性。当创建新文件时,fileMode指定了文件的初始权限设置。

1.2 VFD 的操作流程

   VFD 的操作主要涉及文件的打开、关闭、引用计数管理、LRU 链表管理等。在实际运行中,这些操作通过不同的函数实现,且这些函数紧密协作,以确保文件描述符资源的高效管理。

  1. 文件打开流程:
    当需要打开一个文件时,He3DB 首先会检查 VfdCache 中是否已经存在相应的 VFD。如果存在且文件未打开,系统会尝试重新打开该文件。文件打开流程包括以下步骤:
    查找 VFD:系统首先在 VfdCache 中查找目标文件的 VFD。
    文件描述符的分配:如果 VFD 存在且文件未打开,系统会从操作系统获取一个新的文件描述符,并分配给 VFD 的 fd 字段。
    更新 LRU 链表:将该 VFD 移动到 LRU 链表的头部,表示它是最近使用的文件。

  2. 文件关闭流程:
    当一个文件的引用计数变为零时,He3DB 会关闭该文件,并释放对应的操作系统文件描述符。
    这一过程包括以下步骤:减少引用计数:调用 FileClose 函数时,首先减少文件的引用计数。判断是否关闭:如果引用计数变为零,系统关闭文件描述符,并从 LRU 链表中移除该 VFD。释放资源:将该 VFD 标记为未使用,并将其添加到空闲 VFD 列表中,以便后续使用。

二、LRU 虚拟文件描述符池

   He3DB 通过 LRU(Least Recently Used)算法管理虚拟文件描述符池,确保在系统资源有限的情况下,能够合理地释放和重新分配 VFD。LRU 链表维护了所有的 VFD,按照最近使用的时间顺序排列,最久未使用的 VFD 被优先关闭,以释放系统资源。

   LRU 链表通过 lruLink 字段来维护。在 He3DB 中,LRU 链表的管理和操作均在 fd.c 文件中实现,通过特定的函数和宏来完成。

2.1 VfdCache 全局数组

   VfdCache 是一个全局数组,用于存储所有的 VFD 实例。这个数组在 He3DB 启动时初始化,并随着系统的运行动态管理其大小。VfdCache 作为 He3DB 管理 VFD 的核心结构,承担了存储、查找、分配和回收 VFD 的功能。

static Vfd *VfdCache;
static Size SizeVfdCache = 0;

/*
 * Number of file descriptors known to be in use by VFD entries.
 */
static int	nfile = 0;

2.2 VfdCache 数组初始化

   在系统启动时,VfdCache 会被初始化,这包括分配数组空间、初始化所有 VFD,并将其标记为未使用状态。该初始化又initFileAccess初始化代码如下:

void
InitFileAccess(void)
{
	Assert(SizeVfdCache == 0);	/* call me only once */

	/* initialize cache header entry */
	VfdCache = (Vfd *) malloc(sizeof(Vfd));
	if (VfdCache == NULL)
		ereport(FATAL,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));

	MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
	VfdCache->fd = VFD_CLOSED;

	SizeVfdCache = 1;
}

注:此时VfdCache[0]不是可以的VFD 是整个LRU池的头节点,指向对空间某个地址。

以下是对每行代码的详细分析:

void InitFileAccess(void)
{
	Assert(SizeVfdCache == 0);	/* call me only once */

   代码使用 Assert 宏检查 SizeVfdCache 的值是否为 0。这是一个断言,用于确保这个初始化函数在缓存大小为 0 时才被调用,保证函数只会被调用一次。如果 SizeVfdCache 的值不是 0,表明缓存已经被初始化过,函数会在调试模式下中断执行。

	/* initialize cache header entry */
	VfdCache = (Vfd *) malloc(sizeof(Vfd));
	if (VfdCache == NULL)
		ereport(FATAL,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));

   代码分配了一个 Vfd 结构体的内存,并将其指针赋值给 VfdCache。sizeof(Vfd) 计算 Vfd 结构体的大小,以确保分配的内存足够。
   如果 malloc 返回 NULL,则表示内存分配失败。此时,ereport 宏用于报告致命错误,提示“out of memory”(内存不足),并使用 ERRCODE_OUT_OF_MEMORY 错误代码。ereport 宏会终止程序的执行,避免进一步的内存错误。

	MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
	VfdCache->fd = VFD_CLOSED;

   MemSet 是一个用于将内存区域的每个字节设置为指定值的函数。这里,它将 VfdCache[0] 这个 Vfd 结构体的内存区域中的所有字节设置为 0。这意味着 Vfd 结构体的所有字段都将被初始化为 0。
   VfdCache->fd = VFD_CLOSED; 将 Vfd 结构体的 fd 字段设置为 VFD_CLOSED,标志着当前没有打开的文件。VFD_CLOSED 是一个特殊的值,通常是负数,表示文件描述符未被打开。

	SizeVfdCache = 1;

   SizeVfdCache 被设置为 1,表示 VfdCache 数组中当前有一个 Vfd 实例。这个值用于跟踪缓存的大小,确保系统知道当前有多少个虚拟文件描述符被分配和管理。

2.3 LRU 结构图

   LRU池是一个双向链表,开始和结束于元素VfdCache[0], 元素0是特殊节点,它不代表一个文件,其中fd字段总是等于VFD_CLOSED。 元素0是一个头节点,它标明了LRU池的开始/结束。只有当前真正打开(分配了FD)的VFD元素在LRU池中。
   虽然LRU池是双向链表,但是Vfd结构中并没有指针,而是通过IruMoreRecently、 IruLessRecently这两个int类型的成员变量实现了双向链表中的next和prev指针的功能。
   对于LRU池中的每个VFD,均使用成员IruMoreRecently, IruLessRecently链接两个VFD变量,通过IruMoreRecently成员数组下标链接最近更常使用的VFD;而通过IruLessRecently成员数组下标链接最近不常用的VFD。如下图所示:
在这里插入图片描述

   其中VfdCache[0]充当该链接池的头节点(特殊VFD);另外该LRU池的尾元素VfdCache[0]通过IruLessRecently成员链接到VfdCache[0]头部,而VfdCache[0]头节点通过IruMoreRecently成员链接到 VfdCache[n]。这样就能够很方便地通过VfdCache[0]头节点找到该池中最近最少使用的VFD。
   当然,这个LRU池的大小同样是受到操作系统对进程打开文件描述符数据的限制是一样的。在He3DB中,与max safe fds变量的值极其相关。

2.4 从LRU池获取VFD与释放

   当 He3DB 的 postmaster 进程启动时,它会为 VfdCache 指针分配一块内存,用于存储 Vfd 类型的结构体。然而,此时 VfdCache 并不包含任何有效的虚拟文件描述符(VFD)。具体来说,VfdCache 的第一个元素 VfdCache[0] 被用作双向链表的头节点,这个节点不会存储实际的 VFD。

   在数据库系统的启动过程中,最初尝试获取 VFD 的时候,进程会调用 AllocateVfd() 函数来分配一个有效的 VFD 变量。如果此时没有可用的 VFD,AllocateVfd() 函数会处理这一情况。

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;
}

下面是对每行代码的详细分析:

static File AllocateVfd(void)
{
	Index		i;
	File		file;

   定义了局部变量 i 和 file。i 用于循环和索引,file 用于存储分配的 VFD 的索引。

	DO_DB(elog(LOG, "AllocateVfd. Size %zu", SizeVfdCache));

   这是一个调试输出语句,用于记录当前 VFD 缓存的大小(SizeVfdCache)。DO_DB 是一个宏,可能用于控制调试日志的输出。

	Assert(SizeVfdCache > 0);	/* InitFileAccess not called? */

   使用 Assert 宏检查 SizeVfdCache 是否大于 0。这是一个断言,确保 AllocateVfd 函数在 InitFileAccess 已经被调用且缓存已经初始化之后调用。如果 SizeVfdCache 小于或等于 0,说明缓存还没有初始化,这会导致断言失败。

	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;

   这段代码检查缓存中的空闲列表是否为空 (VfdCache[0].nextFree == 0)。如果为空,则需要扩展缓存。
   计算新的缓存大小 newCacheSize 为当前缓存大小的两倍。这样做可以逐步扩大缓存容量,以减少频繁扩展的开销。
   确保 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;

   使用 realloc 扩展 VfdCache 的内存块。如果内存分配失败,realloc 会返回 NULL,此时报告错误并终止程序。
   如果 realloc 成功,将 VfdCache 指向新的内存块。

		/*
		 * 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;

   对新分配的 Vfd 实例进行初始化:

  • 使用 MemSet 将每个新 Vfd 的内存清零。
  • 设置每个新 Vfd 的 nextFree 指向下一个 VFD的索引,使它们形成一个空闲列表链表。
  • 设置每个 Vfd 的 fd 字段为 VFD_CLOSED,表示文件描述符未打开。

   将 VfdCache 末尾的 nextFree 设置为 0,标记链表的结束。
   更新 VfdCache[0].nextFree 以指向扩展前缓存的第一个索引,表示新的空闲列表的开始。

		/*
		 * Record the new size
		 */
		SizeVfdCache = newCacheSize;
	}

   更新 SizeVfdCache 为新的缓存大小。记录当前缓存的实际大小,以便后续分配和管理。

	file = VfdCache[0].nextFree;

	VfdCache[0].nextFree = VfdCache[file].nextFree;

	return file;
}

   从 VfdCache[0].nextFree 中获取下一个空闲 VFD 的索引,并将其赋值给 file。
   更新 VfdCache[0].nextFree 为下一个空闲 VFD 的 nextFree,从而维护空闲列表的链表。
   返回分配的 VFD 索引 file。

   VfdCache 的分配策略是通过倍增的方法来分配 VFD 内存空间。初始时,VfdCache 中的 VFD 数量设置为一个最小值,比如 32 个。当系统首次初始化 VfdCache 时,如果当前 SizeVfdCache 变量的值为 1(表示当前申请的 VFD 数量为 32),则在内存不足的情况下会申请 32 个新的 VFD 变量内存空间。

这个分配策略的详细步骤如下:

  1. 初始化 VfdCache:
    在数据库进程启动时,VfdCache 的初始分配量是 32 个 VFD 结构体。
    SizeVfdCache 变量记录当前分配的 VFD 数量。在第一次初始化时,SizeVfdCache 的值设置为 1,表示当前已分配 32 个 VFD。
    首次调用 AllocateVfd():

  2. 在 AllocateVfd() 函数被调用时,如果发现 SizeVfdCache 为 1(即当前已经分配了 32 个 VFD),但需要更多的 VFD 空间时,系统会进行新的内存申请。
    内存申请的数量通常是原有数量的倍数,例如再申请 32 个新的 VFD 空间。
    内存分配示意图:

  3. 首次申请时:
    已分配:32 个 VFD
    SizeVfdCache = 1
    如果需要更多的 VFD:
    再次申请:32 个新的 VFD
    更新 SizeVfdCache 为 2

这样,每次内存不足时,VfdCache 会按照预设的倍增策略继续申请更多的 VFD 结构体空间,确保系统有足够的资源来处理文件操作。

当获取到可用的VFD数组元素之后,接下来就开始调用系统函数来打开所指定的文件,然后将open()系统函数返回的文件描述符fd初始化给VFD中的fd成员变量。同时分别将本次打开文件的模式以及文件权限(若是创建文件的话)初始化给VFD中的成员fileFlags和fileMode。并将其他的成员根据实际情况进行初始化。

File
PathNameOpenFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
{
	char	   *fnamecopy;
	File		file;
	Vfd		   *vfdP;

	DO_DB(elog(LOG, "PathNameOpenFilePerm: %s %x %o",
			   fileName, fileFlags, fileMode));

	/*
	 * We need a malloc'd copy of the file name; fail cleanly if no room.
	 */
	fnamecopy = strdup(fileName);
	if (fnamecopy == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));

	file = AllocateVfd();
	vfdP = &VfdCache[file];

	/* Close excess kernel FDs. */
	ReleaseLruFiles();

	vfdP->fd = BasicOpenFilePerm(fileName, fileFlags, fileMode);

	if (vfdP->fd < 0)
	{
		int			save_errno = errno;

		FreeVfd(file);
		free(fnamecopy);
		errno = save_errno;
		return -1;
	}
	++nfile;
	DO_DB(elog(LOG, "PathNameOpenFile: success %d",
			   vfdP->fd));

	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;

	Insert(file);

	return file;
}

PathNameOpenFilePerm 函数的目的是打开一个文件,并为其分配一个虚拟文件描述符(VFD)。下面是对函数每行代码的详细分析:

File PathNameOpenFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
{
	char	   *fnamecopy;
	File		file;
	Vfd		   *vfdP;

   定义了局部变量 fnamecopy(文件名的拷贝)、file(虚拟文件描述符的索引)和 vfdP(指向 VfdCache 的指针)。

	DO_DB(elog(LOG, "PathNameOpenFilePerm: %s %x %o",
			   fileName, fileFlags, fileMode));

   调试输出语句,记录函数的输入参数 fileName(文件名)、fileFlags(文件打开标志)和 fileMode(文件权限模式)。DO_DB 是一个宏,用于控制调试日志的输出。

	/*
	 * We need a malloc'd copy of the file name; fail cleanly if no room.
	 */
	fnamecopy = strdup(fileName);
	if (fnamecopy == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_OUT_OF_MEMORY),
				 errmsg("out of memory")));

   使用 strdup 创建 fileName 的一个动态分配的副本。strdup 会分配内存并复制字符串内容。
   如果 strdup 失败(返回 NULL),则报告内存不足的错误并终止程序。

	file = AllocateVfd();
	vfdP = &VfdCache[file];

   调用 AllocateVfd 函数分配一个新的虚拟文件描述符(VFD),并将其索引存储在 file 变量中。
   设置 vfdP 指向 VfdCache[file],即新分配的 VFD。

	/* Close excess kernel FDs. */
	ReleaseLruFiles();

   调用 ReleaseLruFiles 函数关闭不再使用的内核文件描述符(FD)。这有助于释放系统资源,避免文件描述符耗尽。

	vfdP->fd = BasicOpenFilePerm(fileName, fileFlags, fileMode);

   调用 BasicOpenFilePerm 函数使用指定的 fileName、fileFlags 和 fileMode 打开文件,并将返回的文件描述符赋值给 vfdP->fd。BasicOpenFilePerm 是实际执行文件打开操作的函数。

	if (vfdP->fd < 0)
	{
		int			save_errno = errno;

		FreeVfd(file);
		free(fnamecopy);
		errno = save_errno;
		return -1;
	}

   如果 BasicOpenFilePerm 返回负值(表示文件打开失败),则保存当前的 errno 值。
   调用 FreeVfd 释放先前分配的 VFD。
   释放 fnamecopy 动态分配的内存。
   恢复保存的 errno 值。
   返回 -1 表示失败。

	++nfile;
	DO_DB(elog(LOG, "PathNameOpenFile: success %d",
			   vfdP->fd));

   增加 nfile 计数器,可能用于跟踪当前打开的文件数量。
   调试输出成功信息,包括新打开文件的描述符 vfdP->fd。

	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;

   将动态分配的 fnamecopy 赋值给 vfdP->fileName,保存文件名。
   将 fileFlags 保存到 vfdP->fileFlags 中,但去掉 O_CREAT、O_TRUNC 和 O_EXCL 标志。这是为了处理文件的重新打开情况。
   保存 fileMode 到 vfdP->fileMode。
   将 fileSize、fdstate 和 resowner 初始化为默认值。这些字段可能用于文件状态管理和资源所有权。

	Insert(file);

	return file;
}

   调用 Insert 函数将新分配的 VFD 插入到适当的位置(如文件描述符表或其他管理结构中)。
   返回分配的虚拟文件描述符 file。
   到这里时, VFD已经是一个可提供给进程使用的虚拟文件描述符了。给上层的是该VFD位于LRU池中一的数组下标nextFree,而不会对外提供VFD中的成员fd值。接下来的最后一个任务就是初始化VFD中的两个数组下标成员IruMoreRecentl和IruMoreRecently。使它们分别指向VfdCache头节点。以便于快速从VfdCache[0]找到该LRU池中最近常使用、不常使用的VFD。以便于在LRU池超出操作系统文件描述符限制时根据LRU策略删除不常用的VFD。对应代码如下:

vfdP = &VfdCache[file];

	vfdP->lruMoreRecently = 0;
	vfdP->lruLessRecently = VfdCache[0].lruLessRecently;
	VfdCache[0].lruLessRecently = file;
	VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;

总结

He3DB 中的 VFD 机制通过对操作系统文件描述符的抽象和扩展,实现了数据库文件操作的高效管理。VFD 的设计包括全局 VfdCache 数组、LRU 链表管理策略、动态分配和回收机制等。通过这些机制,He3DB 能够在高负载环境下有效管理文件资源,确保数据库的稳定性和性能。

VFD 的实现细节充分展示了 He3DB 在资源管理方面的精细设计和优化思路。通过这一机制,系统不仅能够高效地管理文件描述符,还能够在资源紧张时灵活调度,确保数据库系统的平稳运行。。

作者介绍

张杰,移动云数据库工程师,负责云原生数据库He3DB的研发。

Challenge 4 - OSCP A 在c盘下发现一个windows.old,在里面可以找到SYSTEM和SAM文件,下载到本地,利用samdump2解密发现没解出来,再来试试pwdump.py(https://github.com/CiscoCXSecurity/creddump7)得到压缩包密码bludecode,解压之后发现一个比较特殊的文件configuration.php,里面有两个类似密码的字符串。登录之后在/opt/backups/下发现几个特殊文件,其中sitebackup3.zip是个正常的压缩包。但是这个压缩包是有密码的。 阅读详情

相关推荐

清华数据结构列车调度AC100分

两种思路(1)将目标栈的数据和待混洗的数据逐个进行比较#pragma warning(disable:4996) #include&lt;stdio.h&gt; #include&lt;string.h&gt; #define maxn 1600005 using namespace std; char a[2*maxn][5];//记录操作命令 int stack[maxn]; char pop...

hgtjcxy的博客 924

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 117

Java 大视界 -- Java+Spark MLlib 构建智能推荐系统:协同过滤算法实战与优化(441)

Java+Spark MLlib构建智能推荐系统实战 本文详细介绍了基于Java和Spark MLlib构建协同过滤推荐系统的全流程实践方案。主要内容包括: 架构设计:采用离线批处理为主、近实时更新为辅的架构,包含数据采集层、处理层、模型层、推荐生成层、存储层和应用层六大模块,可支撑千万级用户推荐需求。 技术实现: 使用Spark MLlib的ALS算法进行协同过滤模型训练 提供数据清洗、模型训练、推荐生成等核心代码实现 采用MySQL存储推荐结果,支持批量插入和事务保障 生产级优化: 版本化模型存储规避覆

【青云交】华为云云享专家 | 阿里云开发者社区专家博主 技术圈个人影响力前 9 | 博客之星 TOP12 优快云 首位四榜(原力榜 / 作者周榜 / 领军人物 / 综合热榜)榜首,破平台纪录! 苏州地区全榜霸榜,感恩全网十多万粉丝同行! 2525

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 196

手把手教你用HiNet实现图像隐藏:可逆神经网络实战指南(附代码)

本文提供了一份基于HiNet框架实现图像隐藏的实战指南。通过详细解析可逆神经网络原理,手把手指导环境搭建、模型构建、损失函数设计及训练流程,并附有完整代码。该方法利用单个可逆网络统一隐藏与恢复过程,结合小波变换提升隐蔽性,为信息隐藏应用提供了高效解决方案。

rgv234567的博客 433

我用 AI 做了个开源地图工具:从 OpenStreetMap 一键获取全国 AOI/POI 数据

本文介绍了一款免费开源的OSM数据查询与可视化工具,旨在解决Overpass QL语法晦涩、数据获取门槛高的问题。工具支持面数据(AOI)、点数据(POI)及路网三种查询模式,内置30+常用模板实现一键检索。具备高德与OSM双底图切换、坐标自动转换功能,支持GeoJSON、SHP、CSV多格式导出,同时保留自定义查询能力。作为纯HTML单文件应用,可零成本部署至GitHub Pages在线使用。该工具有效降低了地理数据获取难度,是替代商业地图API、高效提取全球开源地理信息的实用利器。

weixin_45812624的博客 585

Docker Compose:搭建本地多服务开发环境

从一个 Java API、PostgreSQL 和 Redis 开始,配置服务名、端口、环境变量、健康检查、数据卷和依赖关系。理解 Compose 文件如何被解析、项目如何隔离,以及哪些数据不能依赖“重建容器”保留,能够把个人开发环境整理成团队可复用的启动入口。

m0_62221377的博客 428

华为云Stack 8.X 流量模型分析(一)

​ 当内部需要提供对外服务时(如对外发布Web网站),外部地址发起主动连接,由路由器或者防火墙上的网关接收这个连接,然后将连接转换到内部,此过程是由带有公网IP的网关替代内部服务来接收外部的连接,然后在内部做地址转换,此转换称为DNAT,主要用于内部服务对外发布。​ 内部地址要访问公网上的服务时(如Web访问),内部地址会主动发起连接,由路由器或者防火墙上的网关对内部地址做个地址转换,将内部地址的私有IP转换为公网的公有IP,网关的这个地址转换称为SNAT,主要用于内部共享IP访问外部。

Ciyaso的博客 3243

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 395

视图库对接系列(GA-T 1400)四、视图库对接系列(本级)注册

在之前的步骤中,我们已经把项目大体的架构已经写出来了。那我们就来实现注册接口。GA-T 1400中的步骤如下:这里的话,我们实现的简单点, 我们不进去鉴权,也就是设备或平台找我们注册的话,我们直接返回成功,不进行验证。如果需要鉴权的话可看我的另外一篇文档。

qq_37716298的博客 1043

【系统分析师】2026年-上半年真题:综合知识-答案及详解(75题完整回忆版)

系统分析师考试备考指南(2026上半年机考) 考试安排: 2026年5月23日举行,分三科连考: 综合知识+案例分析(08:30-12:30,240分钟) 论文(14:30-16:30,120分钟) 科目详情: 综合知识:75道单选(含5道英文题),满分75分,45分及格。 案例分析:5选3主观题,满分75分,45分及格。 论文:4选1论述题,满分75分,45分及格。 重点题型解析: 软件工程:结构化分析以数据字典为核心(题1)。 概率统计:独立事件概率计算(题2,三人解题概率≈0.9)。 数据库安全:DB

数据知道的博客 1889

45-存储过程与函数

匿名块跑完就没了,想下次再用只能复制粘贴。给块起个名字存进数据库,就成了存储过程与函数:一次编译、处处调用、权限收口,还能让 Java 侧少拼 SQL。这篇讲封装、调用、限制与排错的完整套路。CREATE OR REPLACE 覆盖式创建,事务由调用方控制;参数三模式 IN 只进、OUT 只出、IN OUT 双向,OUT 参数调用时要备好变量接;调用三姿势 EXEC、CALL、匿名块,命名传参=>最清晰;函数必有 RETURN,想在 SQL 里调用就不能碰 DML 和事务控制;

yuhou25的博客 152

ollama如何在自己的服务器上跑Qwen3(小白必看)

在国内下载ollama最新安装包时,使用curl -fsSL https://ollama.com/install.sh | sh命令可能会遇到网络问题导致下载进度停滞。为了解决这一问题,可以通过国内的镜像源进行下载,例如使用modelscope社区提供的最新ollama镜像仓库。具体步骤包括:首先通过pip install modelscope安装modelscope,然后使用modelscope download命令下载ollama-linux镜像,接着进入下载目录并赋予安装脚本执行权限,最后运行安装脚

Ven%的博客 756

数据库主键选型指南:BIGINT vs VARCHAR

主键选型关乎数据库性能与架构演进。BIGINT(如雪花算法)在顺序写入、二级索引开销和分布式生成上优势显著,适合高并发核心链路;而传统 VARCHAR UUIDv4 因随机性导致频繁页分裂、存储膨胀,性能堪忧。UUIDv7 通过时间戳有序性实现高效追加写入,结合 BINARY(16) 存储可兼顾性能与安全,成为现代微服务新选择。关键在于:主键应稳定、无业务含义,优先选用趋势递增的紧凑类型,并根据场景合理采用双主键模式或 ULID 等替代方案。

小李同学的博客 300

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

自动化运维选型:四条技术路线的能力边界,以及它们为什么不能互相替代

自动化运维怎么选型?拆解配置管理、基础设施即代码、作业调度与场景化编排四条技术路线边界,分析账本归属与时间语义差异,给出四种架构组合与判断点,帮企业看清为何不能互相替代。

trainsignalcn的专栏 324

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

不用再为文本搜索单独维护一套系统!全文检索深度解析

为了给商品、客服工单或者企业知识库增加一个搜索框,企业往往需要在业务数据库之外,再部署一套Elasticsearch。数据库保存商品、订单和用户等真实业务数据,搜索引擎保存另一份文本索引;两套系统之间,还需要通过CDC、消息队列或者同步程序持续传输数据。一个看似简单的搜索需求,最后可能变成两套集群、两份数据和一条必须保持稳定的数据链路。由此产生的问题并不少:数据库已经完成更新,搜索结果却可能尚未同步;业务查询同时涉及文本、库存、权限和订单状态时,应用需要在两个系统之间来回组合结果;

TiDB 社区干货传送门 211

48-逻辑结构表空间段区块

默认 10%,意思是块里的空闲空间低于 10% 时就不再插入新行,留着给已有行的 UPDATE 变胖用。注意 INSERT 过程中每次申请新区,都是一次潜在的空间报错点,如果表空间满了,此刻就会抛 ORA-01653。剩下的空间被谁占了?段的每个区可以落在表空间的任一数据文件,所以 2T 的表在 32G 单文件上限(smallfile)的世界里照样能活——多加文件就行。,一张只有两个 CLOB 字段的表,实际在库里是三个段(表段 + 两个 LOBSEGMENT),空间统计漏算 LOB 段是新手常犯的错。

yuhou25的博客 284

市场调查与统计分析专业大学期间考证怎么安排?

市场调查与统计分析专业的考证规划,适合从一份完整的调查报告倒推:能不能设计问卷、判断样本是否合理、清洗数据,再把分析结果转化为业务建议?围绕这条能力链安排证书,比单纯按“热门程度”报名更有针对性。结合2026年的学习安排,并为2027年的实习、升学和求职预留时间,大学期间可以重点考虑六类证书:全国计算机等级考试证书、大学英语四级或六级成绩报告单、CDA数据分析师、初级统计专业技术资格证书、微软办公软件国际认证中的Excel相关证书,以及软考数据库系统工程师资格证书。

cc572502653的博客 157

QT(了解各个控件及布局)下

练习工程里加一个“确认发送”按钮点击后弹出一个自己写的对话框:一句提示 + “确定”/“取消”确定 → 打印“已确认”,继续发送;取消 → 打印“已取消”,不发送点窗口右上角 X = 取消对话框弹出期间,主窗口点不动(模态)首先我们要在原项目的基础上,新建一个类,就是一个新的小对话框窗口,如下:然后系统会帮我们创建这个新类的头文件和源文件,接着我们到利用ui编辑器设计这个新窗口的界面,如下:如果要实现文字居中,就要设置到QLabel的属性设置这个alignmen为水平中心对齐。

lhbweilai的博客 214

Oracle 19c 在线重定义后旧表空间清理及 ASM 存储资源回收 SOP

Oracle 19c 在线重定义后旧表空间清理与 ASM 存储资源回收 SOP

「YYL DBA手记」 数据库 · 架构 · 运维 · 实战 yyldba.blog.youkuaiyun.com 482
上一篇: 海山数据库(He3DB)+AI(五):一种基于强化学习的数据库旋钮调优方法
下一篇: 海山数据库(He3DB)技术分享:客户端认证
DAWN1221
博客等级 码龄7年 893粉丝 95原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值