【从零实现高并发内存池】Page Cache 从理解设计到全面实现
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 🏳️🌈一、Page Cache
- 1.1 整体设计
- 1.2 核心功能
- 1.3 具体实现
- 👥总结
🏳️🌈一、Page Cache
1.1 整体设计
page cache 与 central cache结构的相同之处
CentralCache:每个桶(SpanList)维护一个 Span 链表,同一链表中的 Span 切分相同大小的小块。
PageCache:每个页数桶(如 1 页、2 页)维护一个 Span 链表,按页数分类管理空闲内存块。
两者都是哈希桶结构,每个桶都是挂着一个个span,这些span都是用双向链表连接起来的
page cache 与 central cache结构的不同之处
CentralCache 的 SpanList[0] 管理所有 8B 对象对应的 Span。
PageCache 的 SpanList[1] 管理所有 1 页的 Span,SpanList[2] 管理 2 页的 Span。
为什么这里最大挂128页的span呢?
因为在内存池设计中,线程通过 ThreadCache 申请单个对象的最大内存为 256KB,这是由代码中的宏定义 MAX_BYTES 明确限制
而 128页 可以被切成 4 个 256KB 的对象,因此是足够的。当然,如果你想在 page cache 中挂更大的 span 也是可以的,根据具体的需求进行设置即可。
page cache
的最大值取决于个人,我们这里为了方便计算,就设置 1 - 128 下标的哈希桶,分别对应 1 - 128 页。第0页空出来
static const size_t NPAGES = 129;
// 最大挂128页的span,为了让桶号与页号对应起来,我们可以将第0号桶空出来,因此我们需要将哈希桶的个数设置成129。
Page Cache 的单例模式
在内存池设计中,将 PageCache
和 CentralCache
设置为单例模式(Singleton)是经过深思熟虑的设计决策,其核心意义在于 统一资源管理、减少竞争、保证全局一致性。以下是具体分析:
单例模式对 PageCache 的意义
- 全局唯一的物理内存管理
PageCache 直接对接操作系统(如通过 VirtualAlloc 或 mmap),负责 分配/释放物理内存页。
若允许多个实例存在:- 不同实例可能重复分配同一物理内存页,导致 内存重叠 或 数据损坏。
- 无法正确合并相邻空闲 Span(因多个实例无法感知彼此的内存块状态)。
- 合并碎片的唯一性
PageCache 的 核心职责 是合并相邻空闲 Span,减少外碎片。
必须通过全局唯一的实例维护所有 Span 的物理地址和状态,否则合并逻辑无法正确工作。
// PageCache 通过单例保证全局唯一
class PageCache{public:// 管理物理内存页的全局分配与合并,必须保证所有线程通过同一个实例操作内存映射和空闲链表。static PageCache* GetInstance() {return &_sInst;}// 获取一个 k 页的 spanSpan* NewSpan(size_t k);std::mutex _pageMtx;private:SpanList _spanLists[NPAGES];PageCache(){}PageCache(const PageCache&) = delete;static PageCache _sInst;
};
为什么需要在hpp文件中额外声明 _sInst
在 C++ 中,静态成员变量(Static Member Variable)必须在两个地方声明:
1.在类的定义中声明为 static
2.在类的外部进行定义和初始化。为静态成员分配实际内存空间
1.2 核心功能
在内存池设计中,PageCache 是内存管理的底层中枢,负责 物理内存的全局分配、合并与碎片优化。以下是其核心功能的详细解析:
1. 管理物理内存的全局分配
PageCache
直接对接操作系统,通过 SystemAlloc(如 VirtualAlloc 或 mmap)申请 大块物理内存(默认为 128 页),并将其初始化为 Span 对象。
Span* bigSpan = new Span;
void* ptr = SystemAlloc(NPAGES - 1); // 申请 128 页内存
bigSpan->_PAGEID = (PAGE_ID)ptr >> PAGE_SHIFT;
bigSpan->_n = NPAGES - 1;
2. 按需分割大 Span
当 CentralCache 请求 k 页内存时,PageCache 优先从现有 Span 中切分,减少直接向系统申请的次数。
- 检查 k 页链表是否非空,直接返回。
- 若 k 页链表为空,遍历更大的页链表(k+1 到 NPAGES-1),找到可分割的 Span。
- 切分 Span 为 k 页和 i-k 页(i 为原 Span 页数),剩余部分挂回对应链表。
3. 维护按页数分类的 Span 链表
每个链表存储相同页数的空闲 Span。例如:_spanLists[3] 存储所有 3 页的 Span。
- 插入:切分后的剩余 Span 挂回对应链表。
- 弹出:直接取用链表中的 Span。
1.3 具体实现
1. 获取非空的span
thread cache向central cache申请对象时,central cache需要先从对应的哈希桶中获取到一个非空的Span,然后从这个非空的Span中取出指定大小的对象返回给thread cache。
遍历central cache对应哈希桶中的双链表,如果该双链表中有非空的Span,那么直接将Span返回即可,没有 span 的话,就去 Page Cache 里要
2.那具体是向page cache申请多大的内存块呢?
我们需要根据 thread cache
所申请的空间大小 size,计算出 central cache
一次应该向 page cache
申请几页的内存块
Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
我们可以通过 NumMoveSize 先得到 thread cache 要从中心缓存中获取多少个对象,然后算出总的对象占的内存,就得出了所需字节,利用位运算,按每页8K的大小,可以得到所需页数。 不足1页时,按一页计算
static size_t NumMovePage(size_t size) {// 计算出thread cache一次向central cache申请对象的个数上限size_t num = NumMoveSize(size);// num 个size 大小的对象所需的字节数size_t npage = num * size;npage >>= PAGE_SHIFT;if(npage == 0)npage = 1;return npage;}
PAGE_SHIFT代表页大小转换偏移,我们这里以页的大小为8K为例,PAGE_SHIFT的值就是13。
3.先不管 page cache
里是怎么给的内存块,假设我们现在已经得到 page cache
返回的页号如何找到一个span所管理的内存块呢?
首先需要计算出该span的起始地址,我们可以用这个span的起始页号乘以一页的大小即可得到这个span的起始地址,然后用这个span的页数乘以一页的大小就可以得到这个span所管理的内存块的大小,用起始地址加上内存块的大小即可得到这块内存块的结束位置。
// 这里的 span 是一个大块内存,_PAGEID 是页号,_n 是页数// 这里 start 和 end 的类型必须要用 char* 来表示,因为我们要对内存进行字节级别的操作// void* 不能进行算术运算,因为 void 没有大小类型,编译器无法进行步长判断// 也不能用 int* 或者 其他的指针// 因为是字节级的操作,只有 char* +1 也是跳转1个字节,但是 int* 就是跳转4个字节char* start = (char*)(span->_PAGEID << PAGE_SHIFT);size_t bytes = span->_n << PAGE_SHIFT;char* end = start + bytes;
这里还有一点需要注意,start以及end必须用 char*
来表示,原因在于,char*
是一个字节的大小,我们需要利用 span 保存的页的大小,进行算数运算得到结束位置的地址
再之后就是根据这个span里每个节点的大小需求,切开这个内存条,一个个尾插
// 获取一个非空的 span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size) {// 查看当前的 spanlist 中是否有空闲的 spanSpan* it = list.Begin();while (it != list.End()) {if(it ->_freeList != nullptr)return it;// 这里的 _freeList 是指 span 中的自由链表头指针,_freeList != nullptr // 说明这个 span 中有未分配的小块内存it = it->_next;}// 如果没有空闲的 span,则需要申请一个新的 span// 只能找 page cache 要// 虽然此时central cache的这个桶当中是没有内存供其他thread cache申请的,// 但thread cache除了申请内存还会释放内存,// 如果在访问 page cache 前将central cache对应的桶锁解掉,// 那么此时当其他thread cache想要归还内存到central cache的这个桶时就不会被阻塞list._mtx.unlock();PageCache::GetInstance()->_pageMtx.lock();Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));PageCache::GetInstance()->_pageMtx.unlock();// 接下来是对刚刚获取的 span 进行切分,// 这会这个 span 还没有挂到桶上,其他线程访问不到,所以不需要加锁// 这里的 span 是一个大块内存,_PAGEID 是页号,_n 是页数// 这里 start 和 end 的类型必须要用 char* 来表示,因为我们要对内存进行字节级别的操作// void* 不能进行算术运算,因为 void 没有大小类型,编译器无法进行步长判断// 也不能用 int* 或者 其他的指针// 因为是字节级的操作,只有 char* +1 也是跳转1个字节,但是 int* 就是跳转4个字节char* start = (char*)(span->_PAGEID << PAGE_SHIFT);size_t bytes = span->_n << PAGE_SHIFT;char* end = start + bytes;// 把大块内存切成自由链表连接起来// 1. 先切一块下来去做头,方便尾插span->_freeList = start;start += size;void* tail = span->_freeList;int i = 1;while (start < end) {++i;NextObj(tail) = start;tail = NextObj(tail);start += size;}// 2. 切好 span 后,需要把 span 挂到桶里面去,得加锁list._mtx.lock();list.PushFront(span);return span;
}
4.从 Page Cache 中获取K页的内存
当我们调用上述的GetOneSpan从central cache的某个哈希桶获取一个非空的span时,如果遍历哈希桶中的双链表后发现双链表中没有span,或该双链表中的span都为空,那么此时central cache就需要向page cache申请若干页的span了,下面我们来分析如何从page cache获取一个k页的span。
因为page cache是直接按照页数进行映射的,因此我们要从page cache获取一个k页的span,就应该 直接先去找page cache的第k号桶, 如果第k号桶中有span,那我们直接头删一个span返回给central cache就行了。所以我们这里需要再给 SpanList类添加对应的Empty和PopFront函数。
class SpanList {public:SpanList() {_head = new Span;_head->_next = _head;_head->_prev = _head;}// 获取开头节点Span* Begin(){return _head->_next;}// 获取哨兵位Span* End(){return _head;}// 判空bool Empty() {return _head->_next == _head;}// 返回头结点Span* PopFront() {Span* front = _head->_next;assert(front != _head);Erase(front);return front;}private:Span* _head;public:std::mutex _mtx; // 桶锁
};
如果
page cache
的第k号桶中没有span,我们就应该继续找后面的桶,只要后面任意一个桶中有一个n页的span,我们就可以将其切分成一个k页的span和一个n-k页的span,然后将切出来k页span返回给central cache,再将n-k页的span挂接到page cache的第n-k号桶中。
// 检查一下后面的桶里面有没有 span,如果有可以把他进行切分for (size_t i = k + 1; i < NPAGES; ++i) {// 如果后面的桶里面有 span,这个 span 是肯定大于 k 页的if (!_spanLists[i].Empty()) {// 弹出第 i 个桶的第一个 spanSpan* nSpan = _spanLists[i].PopFront();// 进行切分,切分成一个 k 页的 span 和一个 i-k 页的 spanSpan* kSpan = new Span;kSpan->_PAGEID = nSpan->_PAGEID;kSpan->_n = k;nSpan->_PAGEID += k;nSpan->_n -= k;_spanLists[nSpan->_n].PushFront(nSpan);return kSpan;}}
那如果 k 及其之后都没有span呢,那就只能去堆中申请了
// 走到这个位置就说明后面没有大页的 span 了// 就需要去找堆要一个 128 页的 spanSpan* bigSpan = new Span;void* ptr = SystemAlloc(NPAGES - 1);bigSpan->_PAGEID = (PAGE_ID)ptr >> PAGE_SHIFT;bigSpan->_n = NPAGES - 1;_spanLists[bigSpan->_n].PushFront(bigSpan);return NewSpan(k);
有一个问题:当central cache向page cache申请内存时,central cache对应的哈希桶是处于加锁的状态的,那在访问page cache之前我们应不应该把central cache对应的桶锁解掉呢
其实最好是解锁掉。因为central cache 此时是没有内存的,必须要去 page cache中去申请,所以此时 thread cache 无法从 central cache 中获取任何内存。可是如果 thread cache 有内存需要返回呢,那不就会因为向 page cahce 申请内存而被阻塞吗,进而造成堵塞。
所以我们可以在确认要向 page cache 申请内存的时候将 central cache 的这个span 的锁给解了,然后等切分完之后,需要将其插进 central cache 的哈希桶的时候再锁上,挂完了在解锁。从而有效避免了申请内存这期间的阻塞
// 获取一个非空的 span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size) {// 查看当前的 spanlist 中是否有空闲的 spanSpan* it = list.Begin();while (it != list.End()) {if(it ->_freeList != nullptr)return it;// 这里的 _freeList 是指 span 中的自由链表头指针,_freeList != nullptr 说明这个 span 中有未分配的小块内存it = it->_next;}// 如果没有空闲的 span,则需要申请一个新的 span// 只能找 page cache 要// 虽然此时central cache的这个桶当中是没有内存供其他thread cache申请的,// 但thread cache除了申请内存还会释放内存,// 如果在访问 page cache 前将central cache对应的桶锁解掉,// 那么此时当其他thread cache想要归还内存到central cache的这个桶时就不会被阻塞list._mtx.unlock();PageCache::GetInstance()->_pageMtx.lock();Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));PageCache::GetInstance()->_pageMtx.unlock();// 接下来是对刚刚获取的 span 进行切分,// 这会这个 span 还没有挂到桶上,其他线程访问不到,所以不需要加锁// 这里的 span 是一个大块内存,_PAGEID 是页号,_n 是页数// 这里 start 和 end 的类型必须要用 char* 来表示,因为我们要对内存进行字节级别的操作// void* 不能进行算术运算,因为 void 没有大小类型,编译器无法进行步长判断// 也不能用 int* 或者 其他的指针// 因为是字节级的操作,只有 char* +1 也是跳转1个字节,但是 int* 就是跳转4个字节char* start = (char*)(span->_PAGEID << PAGE_SHIFT);size_t bytes = span->_n << PAGE_SHIFT;char* end = start + bytes;// 把大块内存切成自由链表连接起来// 1. 先切一块下来去做头,方便尾插span->_freeList = start;start += size;void* tail = span->_freeList;int i = 1;while (start < end) {++i;NextObj(tail) = start;tail = NextObj(tail);start += size;}// 2. 切好 span 后,需要把 span 挂到桶里面去,得加锁list._mtx.lock();list.PushFront(span);return span;
}
👥总结
本篇博文对 【从零实现高并发内存池】Page Cache 从理解设计到全面实现 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:
【从零实现高并发内存池】Page Cache 从理解设计到全面实现
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
6 CMD 与 PowerShell 指令大全、C 程序终端运行、字符编码切换指南
1 CMD 与 PowerShell 常用指令 在命令行环境中高效运行程序,掌握终端的基本操作命令至关重要。无论是 Windows 系统下的 CMD(命令提示符)还是 PowerShell,它们都配备了一系列实用的命令,助力我们管理文件、执行程序以及…...
为啥mac日历打不开浏览器
问题 换了新电脑后,mac上的日历总是没法同步google日历信息,导致经常错过会议 尝试mac日历上添加账户,结果到了打开浏览器缓解总是卡住,打不开浏览器(safari) 解决 检查默认浏览器设置确保已将所需的浏览…...
spring:注解@PostConstruct、@PreDestroy
这两个注解的功能类似标签中的init-method和destroy-method。分别在构造方法调用之后和实例释放资源之前被调用。 注解类: package com.annotation.dao.impl;import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation…...
Androidjetpack之viewmodel的原理分析
前言 viewmodel是jetpack中比较重要的一个组件。如果还没有学习viewmodel不知道怎么写代码什么的,可以看一下我之前写得文章。 jetpack之ViewModel的简单使用https://blog.csdn.net/i_xiang_la_shi/article/details/147218033?fromshareblogdetail&sharetype…...
springboot启动动态定时任务
1.自定义定时任务线程池 package com.x.devicetcpserver.global.tcp.tcpscheduler;import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotatio…...
Dify智能体平台源码二次开发笔记(7) - 优化知识库pdf识别(2)
目录 前言 设计方案 代码具体优化 前言 补充前篇的一些优化。 场景是识别pdf文档,但还需要把pdf文档中的图片也保存下来,在知识库增强检索的时候,直接可以显示图片。 设计方案 1、保存知识库中的图片 2、存入我们的文件服务器中࿰…...
Linux——进程通信
我们知道,进程具有独立性,各进程之间互不干扰,但我们为什么还要让其联系,建立通信呢?比如:数据传输,资源共享,通知某个事件,或控制某个进程。因此,让进程间建…...
AF3 create_alignment_db_sharded脚本create_shard函数解读
AlphaFold3 create_alignment_db_sharded 脚本在源代码的scripts/alignment_db_scripts文件夹下。 该脚本中的 create_shard 函数的功能是将一部分链(shard_files)中的所有对齐文件写入一个 .db 文件,并返回这些链的索引信息(字节…...
Jetpack Compose 实现主页面与局部页面独立刷新的最佳实践
在 Jetpack Compose 开发中,我们经常遇到这样的需求:主页面包含局部页面,主页面刷新时需要更新局部页面,同时局部页面也需要能独立刷新。本文将介绍几种优雅的实现方案。 核心需求 主页面刷新时能触发局部页面更新局部页面能独立…...
KingbaseES之数据库审计
项目提出要配置数据库审计,来满足分保测评得要求.正好最近做过审计测试,还原下审计配置. 一.开启审计 [kingbaserack1 ~]$ vi /data/data_mysql/kingbase.conf [kingbaserack1 ~]$ sys_ctl -D /data/data_mysql restart grep -r shared_preload_libraries /data/data_mysql/k…...
类的加载过程
1、加载 双亲委派模型(启动类》扩展类》应用类) 2、验证 文件格式验证(Class 文件格式检查)元数据验证(字节码语义检查)字节码验证(程序语义检查)符号引用验证(类的正确…...
小白工具视频转 3GP,多格式转换与数据安全的完美结合,在线使用
在众多在线视频转换工具中,小白工具的视频转 3GP 功能(https://www.xiaobaitool.net/videos/convert-to-3gp/ )凭借其出色的性能和丰富的功能脱颖而出,是进行视频格式转换的优质选择。 一、强大的多格式支持 这款工具支持 MP4、…...
六根觉性:穿透表象的清净觉知之光
在喧嚣的禅堂里,老禅师轻叩茶盏,清脆的声响划破沉寂。这声"叮"不仅震动耳膜,更叩击着修行者的心性——这正是佛教揭示的六根觉性在世间万相中的妙用。当我们凝视《楞严经》中二十五圆通法门,六根觉性犹如六道澄明之光&a…...
Redis的IO多路复用
1 传统的socket编码模型 传统 Socket 模型通常采用 多线程/多进程 或 阻塞 I/O 的方式处理网络请求。以下是典型实现步骤: 创建套接字(Socket) 步骤:调用 socket() 创建一个 TCP/UDP 套接字。通常把这个套接字称为【主动套接字】…...
数据结构和算法(六)--栈队列堆
一、栈 栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶(top)。它是后进先出(LIFO)的。对栈的基本操作只有 push(进栈)和 pop(出栈&#…...
js中显示为[object Object]
现象描述: 读取文件并解析数据,遇到变量在使用时异常,通过log输出进行调试,显示为[object,Object]。 分析: [object,Object]表示这是一个对象,其构造函数返回一个对象。 解决方法: 用JSON进行…...
安装matlab R2021b
安装步骤 说明: 以下步骤都是根据R2021b_Windows\Crack_ReadmeWin.txt文件里的内容翻译的。 1)打开安装包根目录,如下: 2)双击R2021b_Windows.iso文件,自动装载进虚拟光驱里,目录入下&…...
Redisson分布式锁深度解析:原理、源码与最佳实践
什么是Redisson分布式锁? 分布式锁是分布式系统中确保资源互斥访问的核心机制,而Redisson作为基于Redis的Java客户端,提供了高效且功能丰富的分布式锁实现。本文将深入剖析Redisson分布式锁的实现原理、核心机制及源码细节,并结合…...
isNaN、Number.isNaN、lodash.isNaN 的区别
isNaN、Number.isNaN、lodash.isNaN 的区别 一、isNaN() 的作用二、什么是 NaN?三、isNaN() 的必要性四、isNaN() 比较1. 全局的isNaN()2. Number.isNaN()3. lodash.isNaN() 五、总结六、附加 一、isNaN() 的作用 检查是否为 NaN 的值,是返回 true&…...
全面解析Flutter中的Stream用法及实际应用
Flutter中的Stream详解 目录 什么是StreamStream的分类Stream的基础用法Stream的常用方法实际应用场景完整示例:计数器应用总结参考文章 1. 什么是Stream 在Flutter开发中,Stream是一种强大的异步数据流处理工具。它类似于广播频道,能够持续推送数据…...
网络请求——微信小程序学习笔记
1. 前言 发起网络请求,即发起HTTPS网络请求 ,注意必须是HTTPS。 2. 使用前注意事项 使用前注意事项可参考官网文档: 基础能力 / 网络 / 使用说明 简单的来说,为了安全,服务器域名必须要备案,如果只是想…...
Oracle19C低版本一天遭遇两BUG(ORA-04031/ORA-600)
昨天帮朋友看一个系统异常卡顿的案例,在这里分享给大家 环境:Exadata X8M 数据库版本19.11 1.系统报错信息 表象为系统卡顿,页面无法刷出,登陆到主机上看到节点1 系统等待存在大量的 cursor: pin S wait on X等待 查看两个节…...
车机系统夏令时设置功能的说明
车机系统夏令时设置功能的说明 基本原理,夏令时,也就daylight saving time。据说古时候,电费比较贵,为了多采用白天自然光照明,通过行政的方式,调节上班时间。使大家能充分使用白天的时间干活,…...
DeepSeek+大数据分析快速应用落地
一、环境准备 1、准备一个 hive 的环境,并可以进行远程连接 2、环境中安装有 sqoop 和 mysql 3、DeepSeek 我使用的是 《问小白》 注册地址:打开问小白,填入我的分享码【1VYXOI】使用满血DeepSeek R1,零延迟、不卡、不限次、不…...
web前端开发:CSS的常用选择器
CSS常用选择器 CSS选择器是用于精准定位HTML元素并对其应用样式的核心工具。它的作用类似于“筛选器”,通过特定规则匹配文档中的元素,从而实现样式控制。 核心作用 定位元素 通过元素名称、类名、ID、属性等条件,快速找到需要样式化的目标元…...
Mathematica 中,将含有小数的表达式转换为整数或分数形式
具体方法和示例: 1. 使用 Rationalize 函数 Rationalize[x] 将小数 x 转换为最接近的有理数(分数形式),可指定精度容忍度。 示例: Rationalize[0.25] (* 输出: 1/4 *) Rationalize[3.14159, 0.001] (* 输出:…...
在 Ubuntu 下通过 Docker 部署 Mastodon 服务器的详细教程
大家好!今天我们来聊聊如何在 Ubuntu 系统上通过 Docker 部署 Mastodon 服务器。Mastodon 是一个开源的社交网络平台,类似于 Twitter,但更注重隐私和去中心化。Docker 则是一个非常流行的容器化平台,能够让我们轻松地打包、分发和…...
JavaScript基础-01(笔记)
前期:js变量 数据类型 数据类型检测 类型转换 数据类型 //// 基本数据类型 存放到栈// a.Number 数字类型(包含整数 小数)var num1var num1.23443var num2222// NaN 非数字类型或者不能转为数字(例:1,"1","1233…...
【C语言基础】C++ 中的 `vector` 及其 C 语言实现详解
一、C 中的 vector:动态数组的核心特性 1. 基本概念 vector 是 C 标准模板库(STL)中的动态数组容器,支持自动扩容、高效元素访问和丰富的操作接口。其核心特性包括: 动态内存管理:自动调整容量࿰…...
记录待办事项的便签软件有没有推荐的?
在快节奏的现代生活中,我们每天都要处理大量的工作任务和生活琐事,稍有不慎就可能遗漏重要事项。你是否经常遇到这样的情况:明明记得有件事要做,却怎么也想不起来是什么;或者手头同时有好几项任务,却不知道…...
华为OD机试真题——硬件产品销售方案(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式! 华为OD机试真题《硬件产品销售方案》: 目录 题目名称࿱…...
鸿蒙应用元服务开发-Account Kit未成年人模式订阅和处理用户信息变更
一、概述 通过订阅用户信息变更,您可以接收有关用户及其账户的重要更新。当用户取消元服务的授权信息、注销华为账号时,华为账号服务器会发送通知到元服务,元服务可以根据通知消息进行自身业务处理。 二、用户信息变更事件介绍 三、订阅用…...
优化 Dockerfile 性能之实践(Practice of Optimizing Dockerfile Performance)
优化 Dockerfile 性能之实践 构建 Docker 镜像时,Dockerfile 的性能会显著影响构建过程的效率。经过优化的 Dockerfile 可以缩短构建时间、最小化镜像大小并提高整体容器性能。在本文中,我们将探讨优化 Dockerfile 性能的最佳实践。 尽量减少层数 影响…...
OpenShift介绍,跟 Kubernetes ,Docker关系
1. OpenShift 简介 OpenShift是一个开源项目,基于主流的容器技术Docker及容器编排引擎Kubernetes构建。可以基于OpenShift构建属于自己的容器云平台。OpenShift的开源社区版本叫OpenShift Origin,现在叫OKD。 OpenShift 项目主页:https://www.okd.io/。OpenShift GitHub仓库…...
Go:包和 go 工具
引言 通过对关联特性分类,组成便于理解和修改的单元,使包与程序其他包保持独立,助力大型程序的设计与维护 。模块化让包可在不同项目共享、复用、发布及全球范围使用。 每个包定义不同命名空间作为标识符,关联具体包,…...
GIS开发笔记(5)结合osg及osgEarth实现虚线环形区域绘制
一、实现效果:输入中点坐标点、内圆半径、外圆半径,绘制坐标点所在高度的水平面的两个圆形形成环形区域。 二、实现原理: 创建中心点所在平面的圆形几何体,将其分别挂接到同一个节点上,再将该节点挂接到用户绘制组节…...
天线静电防护:NRESDTLC5V0D8B
一. 物联网天线的使用环境 1.1 联网天线广泛应用于智能家居领域,比如智能门锁、智能摄像头等设备中,通过天线实现设备与家庭网络的连接,用户可以远程控制和监控家居设备。以智能摄像头为例,它通过天线将拍摄的画面实时传输到用户…...
Linux进程相关选择题及解析
1. 关于Linux进程创建,以下说法正确的是? A. fork()函数调用后,子进程从父进程的fork()之后开始执行 B. fork()函数返回两次,父进程返回子进程PID,子进程返回0[10][11] C. exec函数族会替换当前进程的代码段,但保留数据段和堆栈 D. wait()函数只能等待直接子进程退出 答…...
Day(22)--网络编程习题
习题 以下是这些 TCP 通信练习题的 Java 代码实现及解析: TCP 通信练习 1 - 多发多收 客户端(Client1.java) java import java.io.IOException; import java.io.OutputStream; import java.net.Socket; public class Client1 {public…...
Kubernetes 节点摘除指南
目录 一、安全摘除节点的标准流程 1. 确认节点名称及状态 2. 标记节点为不可调度 3. 排空(Drain)节点 4. 删除节点 二、验证节点是否成功摘除 1. 检查节点列表 2. 检查节点详细信息 3. 验证 Pod 状态 三、彻底清理节点(可选…...
SM4密码算法的CPA攻击技术
SM4算法简介 可参见博文 SM4分组密码算法研究。 SM4密码算法的CPA攻击技术 相关功耗攻击(CPA)是侧信道功耗分析攻击中较为常见的攻击方法之一,攻击者利用密码算法执行过程中,在侧信道泄露的信息(如时序、能量、缓存等)和通信信道的消息(如明文、私钥等)进行测试,通过…...
Golang|KVBitcask
文章目录 初识KVbitcask论文详解 初识KV bitcask论文详解 论文地址:https://riak.com/assets/bitcask-intro.pdf理想的存储引擎,应该满足下面一些特点:...
【Python进阶】元组:不可变序列的十大核心应用
目录 前言:技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现(10个案例)案例1:基础创建与访问案例2:解包…...
centos安装libheif
参考 解决docker: Error response from daemon: Get “https://registry-1.docker.io/v2/“:连接超时问题_error response from daemon :get-CSDN博客 HEIF编解码器安装 - navyum - 博客园 https://github.com/strukturag/libheif #升级gcc yum install centos…...
初步认识Model Context Protocol (MCP) Java SDK
1. Maven如何下载MCP Java SDK 基础配置(核心模块) 在您的pom.xml文件中添加以下依赖: <dependencyManagement> <dependencies> <dependency> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactI…...
第三章 爬虫提速、selenium模块、requests模块进阶(终)
目录 一.requests进阶 (一)处理cookie (二)防盗链 (三)代理 二.爬虫提速 (一)线程池和进程池 (二)协程 (三)异步http请求-aio…...
unity使用内建组件给刚体增加重力
2019年3月9日11:10:24 unity开发中,有时候发现刚体上的重力不能满足我们的需要,可以通过使用内建组件Constant Force来增加重力: 在游戏对象上。请按照以下操作: 为Player添加一个名为Constant Force组件,选择Player在…...
Java开发中的设计模式之观察者模式详细讲解
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会自动收到通知并更新。这种模式在Java开发中非常常见,尤其是在事件驱…...
【学习笔记】计算机网络(九)—— 无线网络和移动网络
第9章 无线网络和移动网络 文章目录 第9章 无线网络和移动网络9.1 无线局域网WLAN9.1.1 无线局域网的组成9.1.2 802.11局域网的物理层9.1.3 802.11局域网的MAC层协议CSMA 协议CSMA/CD 协议 - 总线型 - 半双工CSMA/CA 协议 9.1.4 802.11局域网的MAC帧 9.2 无线个人区域网WPAN9.3…...