【 OpenGauss源码学习 —— (hash_search)】

列存储(hash_search)

  • 概述
  • hash_search 函数
    • hash_search_with_hash_value 函数
      • calc_bucket 函数
      • get_hash_entry 函数
  • 补充知识

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书以及OpenGauss社区学习文档。

概述

  在对源码的不断学习中,经常可以看到一个名为 hash_search 的函数,该函数用于在哈希表中查找键并执行相应操作。先前一直没有仔细看过该函数的内部逻辑,本文则来详细的学习一下吧。

hash_search 函数

  hash_search 函数是用于哈希表中进行通用的查找插入删除操作的实现。通过调用 hash_search_with_hash_value 函数,该函数提供了对哈希表中元素的查找、插入和删除的支持,具体操作由传入的 action 参数决定action 参数可以取值为 HASH_FIND查找)、HASH_ENTER插入)、HASH_ENTER_NULL插入,返回 NULL 如果内存不足)和 HASH_REMOVE删除)。函数返回 找到/插入/删除 的元素的指针,或者如果没有找到匹配项则返回 NULL。此函数通过提供通用的哈希表操作,实现了对哈希表中数据的灵活管理,支持不同应用场景的需求。函数源码如下所示:(路径:src/common/backend/utils/hash/dynahash.cpp

/** hash_search -- 在哈希表中查找键并执行相应操作* hash_search_with_hash_value -- 在哈希表中查找键并执行相应操作,提供预先计算的哈希值** action 的取值包括:*    HASH_FIND: 在表中查找键*    HASH_ENTER: 在表中查找键,如果不存在则创建新条目*    HASH_ENTER_NULL: 在表中查找键,如果不存在则创建新条目,如果内存不足则返回 NULL*    HASH_REMOVE: 在表中查找键,如果存在则删除条目** 返回值是找到/插入/删除的元素的指针,如果没有找到匹配项则返回 NULL。* (注意:在 REMOVE 操作中,结果是一个悬空指针,不应该被解引用!)** 如果 foundPtr 不为 NULL,则 *foundPtr 被设置为 TRUE,表示在表中找到现有条目,为 FALSE 则表示没有找到。* 这对于 HASH_ENTER 操作是必需的,但在其他情况下与返回值重复。** 对于 hash_search_with_hash_value,hashvalue 参数必须先使用 get_hash_value() 计算。*/
void* hash_search(HTAB* hashp, const void* keyPtr, HASHACTION action, bool* foundPtr)
{// 调用 hash_search_with_hash_value 函数,传递哈希值参数为使用哈希函数计算得到的哈希值return hash_search_with_hash_value(hashp, keyPtr, hashp->hash(keyPtr, hashp->keysize), action, foundPtr);
}

函数 hash_search 的入参如下:

  1. HTAB* hashp指向哈希表的指针,是哈希表的控制结构。
  2. const void* keyPtr指向要查找、插入或删除的键值的指针
  3. HASHACTION action指定要执行的操作,可以取以下值:
  • HASH_FIND查找键值
  • HASH_ENTER查找键值,如果不存在则创建新条目
  • HASH_ENTER_NULL查找键值,如果不存在则创建新条目,如果内存不足则返回 NULL
  • HASH_REMOVE查找键值,如果存在则删除条目
  1. bool* foundPtr指向一个布尔型变量的指针,用于接收是否找到现有条目的信息。如果不关心此信息,可以将其设置为 NULL

hash_search_with_hash_value 函数

  hash_search_with_hash_value 函数是一个通用的哈希表查找插入删除元素的核心函数,根据给定的哈希值键值,它在哈希表中进行查找,如果元素存在返回指向元素的指针,如果不存在根据操作类型进行相应的处理支持的操作类型包括查找(HASH_FIND)插入(HASH_ENTER)插入并返回 NULL(HASH_ENTER_NULL)和删除(HASH_REMOVE)。函数内部通过哈希值定位到对应的哈希桶,再在哈希桶的冲突链上进行查找或执行插入删除操作。在插入时,如果哈希表满了,会进行自动扩展。函数同时考虑哈希表的分区模式冻结状态哈希表顺序扫描的情况,确保操作的正确性和高效性。函数源码如下所示:(路径:src/common/backend/utils/hash/dynahash.cpp

/** 根据哈希值进行哈希表的查找、插入和删除操作,支持不同的操作和返回结果。** 入参:*   - hashp: 指向哈希表的指针,包含哈希表的属性和方法。*   - keyPtr: 要查找、插入或删除的键值的指针。*   - hashvalue: 已经计算好的键值的哈希值。*   - action: 操作类型,可以是 HASH_FIND、HASH_ENTER、HASH_ENTER_NULL 或 HASH_REMOVE。*   - foundPtr: 用于接收是否找到元素的指针,如果不关心,可以设置为 NULL。** 返回值:*   - 查找操作(HASH_FIND)返回找到的元素的指针,如果未找到则返回 NULL。*   - 插入操作(HASH_ENTER 或 HASH_ENTER_NULL)返回新插入的元素的指针,如果出错或内存不足则返回 NULL。*   - 删除操作(HASH_REMOVE)返回被删除的元素的指针,如果未找到或出错则返回 NULL。** 注意:该函数对哈希表进行查找、插入和删除操作,同时考虑了哈希表的扩展和元素的内存管理。*/
void* hash_search_with_hash_value(HTAB* hashp, const void* keyPtr, uint32 hashvalue, HASHACTION action, bool* foundPtr)
{HASHHDR* hctl = hashp->hctl;        // 哈希表的控制信息Size keysize;                       // 键值的大小uint32 bucket;                      // 哈希桶的编号long segment_num;                   // 哈希段的编号long segment_ndx;                   // 哈希段内的索引HASHSEGMENT segp;                   // 哈希段HASHBUCKET currBucket;              // 当前哈希桶HASHBUCKET* prevBucketPtr = NULL;   // 前一个哈希桶的指针HashCompareFunc match = NULL;       // 键值比较函数int freelist_idx = FREELIST_IDX(hctl, hashvalue);  // 自由链表的索引// 统计哈希表的访问次数
#if HASH_STATISTICShash_accesses++;hctl->accesses++;
#endif/** 如果是插入操作,检查是否需要拓展哈希表的桶数量。*/if (action == HASH_ENTER || action == HASH_ENTER_NULL) {/** 如果哈希表是分区模式、冻结状态、或者有活跃的哈希表顺序扫描,就不能进行拓展。* 此处顺序检查的次序是为了先检查成本较低的条件。*/if (!IS_PARTITIONED(hctl) && !hashp->frozen &&hctl->freeList[0].nentries / (long)(hctl->max_bucket + 1) >= hctl->ffactor && !has_seq_scans(hashp)) {(void)expand_table(hashp);}}/** 进行初始查找*/bucket = calc_bucket(hctl, hashvalue);segment_num = bucket >> hashp->sshift;segment_ndx = MOD(bucket, hashp->ssize);segp = hashp->dir[segment_num];if (segp == NULL) {hash_corrupted(hashp);}prevBucketPtr = &segp[segment_ndx];currBucket = *prevBucketPtr;/** 跟随冲突链查找匹配的键值*/match = hashp->match;     // 保存一次键值比较函数的调用keysize = hashp->keysize;  // 保存一次键值的大小while (currBucket != NULL) {if (currBucket->hashvalue == hashvalue && match(ELEMENTKEY(currBucket), keyPtr, keysize) == 0) {break;}prevBucketPtr = &(currBucket->link);currBucket = *prevBucketPtr;
#if HASH_STATISTICShash_collisions++;hctl->collisions++;
#endif}// 将是否找到元素的信息写入 foundPtrif (foundPtr != NULL) {*foundPtr = (bool)(currBucket != NULL);}// 根据不同的操作类型进行处理switch (action) {case HASH_FIND: {// 查找操作,返回找到的元素指针,未找到返回 NULLif (currBucket != NULL) {return (void*)ELEMENTKEY(currBucket);}return NULL;}case HASH_REMOVE: {// 删除操作,返回被删除的元素指针,未找到返回 NULLif (currBucket != NULL) {// 如果是分区模式,需要锁定以修改 nentries 和 freeListif (IS_PARTITIONED(hctl)) {SpinLockAcquire(&(hctl->freeList[freelist_idx].mutex));}Assert(hctl->freeList[freelist_idx].nentries > 0);hctl->freeList[freelist_idx].nentries--;// 从哈希桶链表中移除记录*prevBucketPtr = currBucket->link;// 将记录添加到哈希表的自由链表中currBucket->link = hctl->freeList[freelist_idx].freeList;hctl->freeList[freelist_idx].freeList = currBucket;// 如果是分区模式,释放锁if (IS_PARTITIONED(hctl)) {SpinLockRelease(&hctl->freeList[freelist_idx].mutex);}/** 注意:最好确保调用者在这个元素上同步访问,因为其他代码将会* 在下一次插入时重新使用它。*/return (void*)ELEMENTKEY(currBucket);}return NULL;}case HASH_ENTER_NULL: {// 插入并返回新插入的元素指针,如果失败返回 NULL// ENTER_NULL 无法与基于 palloc 的分配器一起使用Assert(hashp->alloc != DynaHashAlloc || hashp->alloc != DynaHashAllocNoExcept);/* FALL THRU */}case HASH_ENTER: {// 插入并返回新插入的元素指针,如果失败返回 NULL// 如果已经存在元素,则返回找到的元素的指针if (currBucket != NULL) {return (void*)ELEMENTKEY(currBucket);}// 如果是冻结状态,则不能插入if (hashp->frozen) {if (hashp->alloc == DynaHashAllocNoExcept) {write_stderr("cannot insert into frozen hashtable \"%s\"", hashp->tabname);return NULL;}ereport(ERROR,(errcode(ERRCODE_INVALID_OPERATION),errmsg("cannot insert into frozen hashtable \"%s\"", hashp->tabname)));}// 从哈希表的自由链表中获取一个新的哈希桶currBucket = get_hash_entry(hashp, freelist_idx);if (currBucket == NULL) {// 内存不足,返回 NULLif (action == HASH_ENTER_NULL) {return NULL;}// libcomm 永久线程不能使用 elogif (hashp->alloc == DynaHashAllocNoExcept || t_thrd.comm_cxt.LibcommThreadType != LIBCOMM_NONE) {return NULL;}// 报告通用错误消息if (hashp->isshared) {ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory")));} else {ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));}}// 将新元素链接到哈希桶链表中*prevBucketPtr = currBucket;currBucket->link = NULL;// 将键值复制到记录中currBucket->hashvalue = hashvalue;if (hashp->keycopy == memcpy) {errno_t errorno = EOK;errorno = memcpy_s(ELEMENTKEY(currBucket), keysize, keyPtr, keysize);securec_check(errorno, "\0", "\0");} else {hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, keysize);}/** 调用者应该在返回后填充数据字段。* 不要在这里插入可能引发错误的代码,因为这样会导致记录不完整,* 从而损坏调用者的数据结构。*/return (void*)ELEMENTKEY(currBucket);}default:break;}// 未识别的操作类型,报错if (hashp->alloc == DynaHashAllocNoExcept) {write_stderr("unrecognized hash action code: %d", (int)action);} else {ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("unrecognized hash action code: %d", (int)action)));}return NULL; // 保持编译器静默
}

calc_bucket 函数

  内联函数 calc_bucket 用于将给定的哈希值转换为哈希表中的桶号。通过使用哈希表控制结构中的 high_masklow_mask 进行按位与运算,确保计算得到的桶号在哈希表的有效范围内。该函数旨在实现将哈希值映射到哈希表桶的过程,以便在哈希表中查找、插入或删除元素时能够有效地定位到对应的桶。函数源码如下所示:(路径:src/common/backend/utils/hash/dynahash.cpp

/* * 描述: 将哈希值转换为桶号的内联函数。* 参数: *   @in hctl: 哈希表控制结构。*   @in hash_val: 待转换为桶号的哈希值。* 返回值: uint32 - 计算得到的桶号。*/
static inline uint32 calc_bucket(HASHHDR* hctl, uint32 hash_val)
{uint32 bucket;// 使用 high_mask 获取初始桶号。bucket = hash_val & hctl->high_mask;// 如果初始桶号超过最大桶号,使用 low_mask 进行调整。if (bucket > hctl->max_bucket) {bucket = bucket & hctl->low_mask;}// 返回最终计算得到的桶号。return bucket;
}

get_hash_entry 函数

  get_hash_entry 函数用于从哈希表的自由列表中获取一个新的哈希表元素。函数根据给定的自由列表索引哈希表控制结构,尝试从自由列表中获取一个元素。如果自由列表中没有空闲元素,则根据哈希表的分区策略尝试从其他分区借用元素。如果所有尝试都失败,函数返回 NULL。如果成功获取到元素,函数会从自由列表中移除该元素,并增加相应的计数。如果是分区哈希表,函数在操作自由列表时会使用自旋锁确保线程安全。函数源码如下所示:(路径:src/common/backend/utils/hash/dynahash.cpp

/** 描述:如果可能,创建一个新的哈希表元素。* 参数:*   @in hashp: 哈希表的控制结构。*   @in freelist_idx: 自由列表的索引,用于确定从哪个自由列表中获取元素。* 返回值:HASHBUCKET - 新创建的哈希表元素,如果创建失败返回NULL。*/
static HASHBUCKET get_hash_entry(HTAB* hashp, int freelist_idx)
{HASHHDR* hctl = hashp->hctl;   // 获取哈希表的控制结构HASHBUCKET newElement;         // 新的哈希表元素指针int borrow_from_idx;           // 用于在分区哈希表中从其他分区借用元素的自由列表索引for (;;) {// 如果使用分区哈希表,必须锁定以操作 nentries 和 freeListif (IS_PARTITIONED(hctl)) {SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);}// 尝试从自由列表中获取一个元素newElement = hctl->freeList[freelist_idx].freeList;if (newElement != NULL) {break; // 成功获取元素,退出循环}// 如果使用分区哈希表,释放锁并尝试从其他分区借用元素if (IS_PARTITIONED(hctl)) {SpinLockRelease(&hctl->freeList[freelist_idx].mutex);}// 自由列表中没有空闲元素,分配一个新的元素块if (!element_alloc(hashp, hctl->nelem_alloc, freelist_idx)) {if (!IS_PARTITIONED(hctl)) {return NULL; // 内存不足,返回NULL}// 尝试从其他分区借用元素borrow_from_idx = freelist_idx;for (;;) {borrow_from_idx = (borrow_from_idx + 1) % NUM_FREELISTS;// 尝试获取其他分区的元素SpinLockAcquire(&(hctl->freeList[borrow_from_idx].mutex));newElement = hctl->freeList[borrow_from_idx].freeList;if (newElement != NULL) {// 成功从其他分区获取元素,更新自由列表和 nentrieshctl->freeList[borrow_from_idx].freeList = newElement->link;SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);hctl->freeList[freelist_idx].nentries++;SpinLockRelease(&hctl->freeList[freelist_idx].mutex);break; // 成功获取元素,退出循环}SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));}return newElement;}}// 从自由列表中移除元素,增加 nentrieshctl->freeList[freelist_idx].freeList = newElement->link;hctl->freeList[freelist_idx].nentries++;// 如果使用分区哈希表,释放锁if (IS_PARTITIONED(hctl)) {SpinLockRelease(&hctl->freeList[freelist_idx].mutex);}return newElement; // 返回新创建的哈希表元素指针
}

补充知识

  1. 哈希桶(Bucket):
    定义: 哈希桶是哈希表中存储数据的地方,通常是一个数组。每个桶都有一个唯一的索引,通过哈希函数计算得到。在一个桶中,可以存储一个或多个数据元素,这取决于哈希冲突的情况。
    作用: 哈希桶用于存储哈希表中的数据通过索引快速定位数据

  1. 哈希函数:
    定义: 哈希函数是将关键字映射为整数值的函数。良好设计的哈希函数应该具备以下特性:
    一致性: 对于相同的输入,哈希函数应始终产生相同的输出
    高效性: 哈希函数计算速度应该快
    离散性: 关键字的微小变化应导致哈希值的显著变化

  1. 哈希冲突:
    定义: 哈希冲突是指两个不同的关键字经过哈希函数映射后得到相同的哈希值。为了解决哈希冲突,通常有两种主要的方法:
    • 链地址法(Separate Chaining): 每个哈希桶都是一个链表,哈希表中的每个桶都存储指向链表头部的指针。当发生哈希冲突时,新的数据元素被插入到对应桶的链表中
    • 开放地址法(Open Addressing): 当发生哈希冲突时,通过一定的规则,寻找另一个可用的桶来存储冲突的数据。常见的开放地址法包括线性探测二次探测等。

  1. 自由列表:
    定义: 自由列表是一种数据结构,用于存储哈希表中的空闲元素。当需要插入新的数据元素时,从自由列表中获取一个空闲元素。在哈希表中,由于数据的插入和删除操作,会导致哈希桶中的元素数量动态变化,因此需要维护一个自由列表,用于存储未被使用的桶

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

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

相关文章

层层剥开Android14升级后异常弹框的神秘面纱

本篇文章将会通过研究源码的方式给您讲述Android系统升级到Android14后出现的两个异常弹框并给出消除它们的方案。闲话少叙,我们开始。 问题描述 在Android 14升级后,出现两个弹窗的异常情况。这里是异常的截图: 接下来,我们对这…

第一个Maven项目

(一)准备工作 1、从官网下载压缩包:apache-maven-3.5.4-bin,然后解压到D盘没有中文的目录。 2、配置环境变量: 在左下角win打开“设置”,搜索“高级系统设置”,点击“高级”,点击“环境变量”&…

酷开科技OS——Coolita,让智能大屏走向国际

10月23日,2023中国—东盟视听传播论坛在南宁举行。作为第五届中国—东盟视听周重要活动之一,本次论坛以“共享新成果、共创新视听、共建新家园”为主题。来自中国和东盟的300余名专家学者、业界代表通过主旨演讲、主题发言、圆桌对话等方式进行深入探讨&…

自学成为android framework高手需要准备哪些装备-千里马车载车机系统开发学习

背景 hi,粉丝朋友们: 大家好!经常有很多学员买课同学都会问到需要准备哪些装备,我也回答了很多学员了,今天就搞一篇文章来统一说明一下,告诉一下大家如果你想从一个framework新手变成一个framework开发的高…

计算机网络实用工具之fping

简介 fping是一个类似ping的程序,它使用互联网控制消息协议(ICMP)回显请求来确定目标主机是否正在响应。fping与ping的不同之处在于,您可以在命令行上指定任意数量的目标,或者指定一个包含要ping的目标列表的文件。fp…

振弦传感器土压力计的安装及埋设方法

振弦传感器土压力计的安装及埋设方法 土压力计是一种测量土体内侧压力的仪器,常用于土体工程的安全监测和评估。以下是土压力计的安装及埋设方法: 1. 选择合适的位置:土压力计的安装位置应该在土体内的高应力区域,以便能够准确测…

Shell变量作用范围

目录 1、函数内的变量 2、shell脚本内的变量 3、进程内的变量 4、进程间的变量 5、终端间的变量 6、用户间的变量 总结 本文内容同微信公众号【凡登】,关注不迷路,学习上高速,欢迎关注共同学习。 什么是变量的作用范围? 变…

在Linux服务器中查找mysql的配置文件并修改其内容并保存,清空mysql8.0以上默认开启SSL的配置,防止odbc无法连接的问题

------每个命令输完记得按【enter】回车键------- 1、查找mysql的配置文件命令-mysql的配置文件默认名是my.cnf: find / -name my.cnf 2、查看显示的配置文件内容: cat /etc/my.cnf 3、修改配置文件的内容: 使用vi 或vim 命令 vi /etc…

推荐系统概述(PPT)

参考资料: 推荐系统系列之推荐系统概览(上) | 亚马逊AWS官方博客推荐系统系列之推荐系统概览(下) | 亚马逊AWS官方博客 目录如下: 推荐系统简介 推荐系统中常见概念 推荐系统中常用的评价指标 首页推荐…

请求的接口响应状态为已取消的原因

有趣的iframe问题 今天遇到一个问题,当点击了按钮----跳转页面时----F12键点击网络中的状态报了已取消,类型是 document说明是前端页面的问题,如果是xhr那可能是接口的问题。 原本是写了3个iframe,页面刷新的时候请求了第一个iframe,然后就…

随机微分方程的MATLAB数值求解

dt0.01; tout200; %总时间为2 xzeros(1,tout); x(1)0.5; %初始位置 mu0.2; sigma1; Wtsqrt(dt)*randn(1,tout); %产生随机序列Wt for t1:tout-1x(t1)x(t)mu*x(t)*dtsigma*x(t)*Wt(t); end t11:10:tout; %对原时间序列进行抽样 xtzeros(1,length(t1)); i1; for tt1xt(i)0.5*exp(…

招投标系统软件源码,招投标全流程在线化管理

功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看所…

【git-分布式版本控制工具】

Git git介绍 分布式版本控制系统工具 vs 集中式版本控制工具git安装 基于官网发布的最新版本2.31.1 安装讲解git命令 基于开发案例 详细讲解了git的常用命令git分支 分支特性 分支创建 分支转换 分支合并 代码合并冲突解决IDea 集成 git Github 创建远程库代码推送 PUSH代码…

HUAWEI华为MateBook X Pro 2022 12代酷睿版(MRGF-16)笔记本电脑原装出厂Windows11系统工厂模式含F10还原

链接:https://pan.baidu.com/s/1ZI5mR6SOgFzMljbMym7u3A?pwdl2cu 提取码:l2cu 华为原厂Windows11系统工厂包,带F10一键智能还原恢复功能。 自带指纹、面部识别、声卡、网卡、显卡、蓝牙等所有驱动、出厂主题壁纸、Office办公软件、华为…

深信服AC跨三层取mac,绑定ip/mac

拓扑图 目录 拓扑图 1.交换机配置团体名和版本号 2.配置跨三层取mac 3.配置策略 验证: “您的每一次阅读、点赞和分享,都是对我最大的鼓舞和动力。” 如果对亲爱您有所帮助,可以尝试支持一下博主,让博主更有动力 1.交换机配置…

Docker上部署mysql(超简单!!!)

拉取mysql镜像 运行如下命令 docker pull mysql:5.7 拉取成功 查看镜像 运行容器 此处部署最新版本的mysql docker run -d --name mysql -p 3307:3306 -e TZAsia/Shanghai -e MYSQL_ROOT_PASSWORD111 mysql --name mysql:给容器起个名字(唯一&#xff…

可靠性工程师的发展之路

都是经验之谈,不懂产品,只靠理论 ,注定行不通。可靠性工程师的成长,是一个专业与产品共同前进的道路。 1、轻易不要想着建可靠性体系。 如果可靠性还处在一穷二白的程度,建可靠性体系只会害了公司,不仅浪…

js中数组去重(数组中元素是对象)

一、使用 Set 对象: const arr [{ id: 1, name: A },{ id: 2, name: B },{ id: 1, name: A },{ id: 3, name: C } ];const result Array.from(new Set(arr.map(item > JSON.stringify(item)))).map(item > JSON.parse(item)); console.log(result); 二、…

bclinux aarch64 openeuler 20.03 LTS SP1 部署 fastCFS

基于已配置好的4个节点部署ceph-0 ceph-1 ceph-2 ceph-3(早期ceph测试环境,名称就不修改了) 获取fcfs.sh mkdir /etc/fcfs cd /etc/fcfs wget http://fastcfs.net/fastcfs/ops/fcfs.sh 配置/etc/fcfs/fcfs.settings # 要安装的集群版本号…

Linux 零拷贝splice函数

Linux splice 函数简介 splice 是 Linux 系统中用于在两个文件描述符之间移动数据的系统调用。它的主要作用是在两个文件描述符之间传输数据&#xff0c;而无需在用户空间进行数据拷贝。也是零拷贝操作. 函数原型 #include <fcntl.h> ssize_t splice(int fd_in, loff_…