当前位置: 首页 > news >正文

【从零实现高并发内存池】- 项目介绍、原理 及 内存池详解

📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 🏳️‍🌈一、项目介绍
    • 1.1 这个项目是做什么的
    • 1.2 要求用到的知识
  • 🏳️‍🌈二、内存池详解
    • 2.1 池化技术
    • 2.2 内存池
    • 2.3 内存池主要解决的问题
    • 2.4 malloc
  • 🏳️‍🌈三、模拟定长内存池
    • 3.1 成员变量
    • 3.1 管理已释放的内存空间
    • 3.2 构建对象内存空间
    • 3.3 析构函数
  • 🏳️‍🌈四、性能测试
  • 👥总结


🏳️‍🌈一、项目介绍

1.1 这个项目是做什么的

当前项目是实现一个高并发的内存池,他的原型是 google 的一个开源项目 tcmalloctcmalloc全称Thread-CachingMalloc即线程缓存的malloc,实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数 (malloc、free)

我们这个项目是把 tcmalloc 最核心的框架简化后拿出来,模拟实现出一个自己的高并发内存池 ,目的就是学习tcamlloc的精华,这种方式有点类似我们之前学习STL容器的方式。但是相比STL容器部分,tcmalloc的代码量和复杂度上升了很多,大家要有心理准备。当前另一方面,难度的上升,我们的收获和成长也是在这个过程中同步上升。

另一方面 tcmalloc 是全球大厂 google 开源的,可以认为当时顶尖的C++高手写出来的,他的知名度也是非常高的,不少公司都在用它,Go语言直接用它做了自己内存分配器。所以很多程序员是熟悉这个项目的,那么有好处,也有坏处。好处就是把这个项目理解扎实了,会很受面试官的认可。坏处就是面试官可能也比较熟悉项目,对项目会问得比较深,比较细。如果你对项目掌握得不扎实,那么就容易碰钉子。

tcmalloc 源代码 https://gitee.com/mirrors/tcmalloc

1.2 要求用到的知识

这个项目会用到C/C++数据结构(链表、哈希桶)操作系统内存管理单例模式多线程互斥锁等等方面的知识,没有网络。难度算是T1

🏳️‍🌈二、内存池详解

2.1 池化技术

所谓 “池化技术”,就是程序 先向系统申请过量的资源,然后自己管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较大的开销,不如提前申请好了,这样使用时就会变得非常快捷,大大提高程序运行效率。

所以这种技术往往用在体量大的工程中,小的体量不如malloc等

在计算机中,有很多使用“池”这种技术的地方,除了内存池,还有连接池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若干数量的线程,让它们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让它来处理客户端的请求,当处理完这个请求,线程又进入睡眠状态。

2.2 内存池

内存池是指 程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;

同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放

2.3 内存池主要解决的问题

内存池主要解决的当然是效率的问题,其次如果作为系统的内存分配器的角度,还需要解决一下内存碎片的问题。那么什么是内存碎片呢?

再需要补充说明的是内存碎片分为 外碎片内碎片,上面我们讲的外碎片问题。

  • 外部碎片 是一些空闲的连续内存区域太小,这些内存空间不连续,以至于合计的内存足够,但是不能满足一些的内存分配申请需求。
  • 内部碎片 是由于一些对齐的需求,导致分配出去的空间中一些内存无法被利用。内碎片问题,我们后面项目就会看到,那会再进行更准确的理解。

在这里插入图片描述

2.4 malloc

C/C++ 中我们要动态申请内存都是通过 malloc 去申请内存,但是我们要知道,实际我们不是直接去堆获取内存的

malloc 就是一个内存池。malloc() 相当于向操作系统“批发”了一块较大的内存空间,然后“零售”给程序用。当全部“售完”或程序有大量的内存需求时,再根据实际需求向操作系统“进货”

malloc 的实现方式有很多种,一般不同编译器平台用的都是不同的。比如 windows 的 vs 系列用的微软自己写的一套,linux gcc 用的 glibc 中的 ptmalloc。

下面有几篇关于这块的文章,大概可以去简单看看了解一下,关于 ptmalloc,学完我们的项目以后,有兴趣大家可以去看看他的实现细节。

一文了解,Linux内存管理,malloc、free实现原理
malloc()背后的实现原理–内存池
malloc的底层实现(ptmalloc)

在这里插入图片描述

🏳️‍🌈三、模拟定长内存池

作为程序员(C/C++)我们知道申请内存使用的是 mallocmalloc 其实就是一个通用的大众货,什么场景下都可以用,但是什么场景下都可以用就意味着什么场景下都不会有很高的性能

下面我们就先来设计一个 定长内存池 做个开胃菜,当然这个定长内存池在我们后面的高并发内存池中也是有价值的,所以学习他目的有两层,先熟悉一下简单内存池是如何控制的第二他会作为我们后面内存池的一个基础组件

在这里插入图片描述

3.1 成员变量

因为 内存池 利用的是 池化技术 ,所以,我们需要一个 空间,此外,为了回收利用这些 空间,还需要一个接收释放空间的链表,就如下图一样
在这里插入图片描述
为了使我们在判断时更加方便,可以再定义一个 记录 剩余内存字节数 的变量

	private:char* _memory = nullptr;	// 指向大块内存的指针size_t _remainBytes = 0;		// 剩余内存字节数void* _freeList = nullptr;		// 空闲链表,指向下一个空闲对象的地址
  • memory
    指向分配的大块内存。
    内存池以“页”为单位申请内存(如 128KB),避免频繁系统调用。
  • remainBytes
    记录当前内存块中剩余的可用字节数。
    用于判断是否需要申请新的内存块。
  • freeList
    空闲链表的头节点,用于管理已释放的对象。
    每个节点存储下一个可用对象的地址(类似链表指针)。

3.1 管理已释放的内存空间

我们对于一个个已经使用完的内存空间,使用 链表 将他们串连起来就行了,确保每一个节点的头几位能够存放下下一个地址的地址大小,也就是 void*

在这里插入图片描述

但是因为下一个对象内存空间尚未确定,可能是 int,也可能是 long long,所以我们可以利用 二级指针,先将该内存块的地址先强转为二级指针,由于二级指针存储的是一级指针的地址,所以再解引用就能得到下一个对象的地址。

  • void*:一个通用指针类型,可以指向任意类型的数据(但丢失了类型信息)。
  • void** :指向 void* 的指针,即它存储的是 void* 指针的地址。
static void*& NextObj(void* obj) {// 转换为 void** 类型,再解引用,得到下一个对象的内存地址// obj是一个指针,指向一个对象的内存地址//  对象内存地址// [ next_ptr (8字节) ][ 对象数据 (sizeof(T)字节) ]// obj 是对象的起始地址// void** pp = (void**)&obj; // pp 指向 obj 的地址(即对象内存的起始位置)// return *(void**)obj; // 取出下一个对象的内存地址	return *(void**)obj;
}

3.2 构建对象内存空间

其实就是构造函数,这里我们需要注意

  • 先将利用已释放的对象内存空间
  • 如果没有,就判断当前的剩余空间够不够,不够的话,再重新分配一块新的内存
  • 判断这样得到的对象内存空间是否有足够的开头空间,来存储下一个对象的地址
  • 确保显示调用构造函数,一定要 new 一下】
T* New() {T* obj = nullptr;// 先把内存池中之前释放的内存块的对象取出来,再次利用if (_freeList) {void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next; // 取出下一个对象} else {// 池化技术:提前分配一块大内存,避免频繁的内存分配和释放// 如果内存池中内存不够一个对象大小时,则重新分配一块新的内存if (_remainBytes < sizeof(T)) {_remainBytes = 128 * 1024;_memory = (char*)SystemAlloc(_remainBytes >> 13);if (_memory == nullptr) {// throw bad_alloc(); 表示主动抛出一个内存分配失败的异常throw std::bad_alloc();}_memory += sizeof(T);_remainBytes -= sizeof(T);}obj = (T*)_memory;// 这里判断一下当前分配的内存,至少需要能够存下下一个对象地址的大小size_t objSize = sizeof(T) < sizeof(void*)? sizeof(void*): sizeof(T); // 取对象大小和指针大小的最大值_memory += objSize;_remainBytes -= sizeof(T);}// 定位 new,显示调用T的构造函数初始化new (obj) T; // 在已分配的内存 obj 上调用 T 的构造函数return obj;
}

3.3 析构函数

先理清楚空闲链表的结构。

通常,空闲链表是一个单链表,每个节点保存下一个可用对象的地址。当释放一个对象时,需要将它插入链表的头部,这样下次分配时可以直接取用。插入操作通常是头插法,即把新节点的next指向原来的头节点,然后更新头节点为新节点。

void Delete(T* obj) {// 显示调用析构函数,释放对象资源obj->~T();// 头插// 这里选择使用 void** 而不是 int* 是因为 void*// 是一个通用指针类型,可以指向任意类型的数据 另外 使用 int**// 等二级指针的效果是一样的// 他的目的都是为了指向目标类型的地址,可以自己分辨是 int 还是 long long// 类型// void*:一个通用指针类型,可以指向任意类型的数据(但丢失了类型信息)。// void** :指向 void* 的指针,即它存储的是 void* 指针的地址。*(void**)obj = _freeList;_freeList = obj;
}

🏳️‍🌈四、性能测试

既然已经模拟实现了 定长内存池,那就来对比一下与普通的 malloc 之间的差距

这里我们先将测试数据设大一点

// 每轮申请释放多少次
const size_t N = 10000000;

在这里插入图片描述

来个十万试试
在这里插入图片描述

但是如果设为100或者更小的数据,就看不出差距了
在这里插入图片描述

#pragma once#include <iostream>
#include <vector>using std::cout;
using std::endl;#ifdef _WIN32
#include<windows.h>
#else
// 
#endif// 定长内存池
//template<size_t N>
//class ObjectPool
//{};// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}template<typename T>
class ObjectPool {public:T* New(){T * obj = nullptr;// 先把内存池中之前释放的内存块的对象取出来,再次利用if (_freeList) {void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next; // 取出下一个对象}else {// 池化技术:提前分配一块大内存,避免频繁的内存分配和释放// 如果内存池中内存不够一个对象大小时,则重新分配一块新的内存if (_remainBytes < sizeof(T)) {_remainBytes = 128 * 1024;_memory = (char*)SystemAlloc(_remainBytes >> 13);if (_memory == nullptr) {// throw bad_alloc(); 表示主动抛出一个内存分配失败的异常throw std::bad_alloc();}_memory += sizeof(T);_remainBytes -= sizeof(T);}obj = (T*)_memory;// 这里判断一下当前分配的内存,至少需要能够存下下一个对象地址的大小size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T); // 取对象大小和指针大小的最大值_memory += objSize;_remainBytes -= sizeof(T);}		// 定位 new,显示调用T的构造函数初始化new(obj)T;	// 在已分配的内存 obj 上调用 T 的构造函数return obj;}void Delete(T* obj) {// 显示调用析构函数,释放对象资源obj->~T();// 头插// 这里选择使用 void** 而不是 int* 是因为 void* 是一个通用指针类型,可以指向任意类型的数据// 另外 使用 int** 等二级指针的效果是一样的// 他的目的都是为了指向目标类型的地址,可以自己分辨是 int 还是 long long 类型// void*:一个通用指针类型,可以指向任意类型的数据(但丢失了类型信息)。// void** :指向 void* 的指针,即它存储的是 void* 指针的地址。*(void**)obj = _freeList;_freeList = obj;}private:char* _memory = nullptr;	// 指向大块内存的指针size_t _remainBytes = 0;		// 剩余内存字节数void* _freeList = nullptr;		// 空闲链表,指向下一个空闲对象的地址
};struct TreeNode
{int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){}
};void TestObjectPool()
{// 申请释放的轮次const size_t Rounds = 5;// 每轮申请释放多少次const size_t N = 10000000;std::vector<TreeNode*> v1;v1.reserve(N);size_t begin1 = clock();for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v1.push_back(new TreeNode);}for (int i = 0; i < N; ++i){delete v1[i];}v1.clear();}size_t end1 = clock();std::vector<TreeNode*> v2;v2.reserve(N);ObjectPool<TreeNode> TNPool;size_t begin2 = clock();for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v2.push_back(TNPool.New());}for (int i = 0; i < N; ++i){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 = clock();cout << "new cost time:" << end1 - begin1 << endl;cout << "object pool cost time:" << end2 - begin2 << endl;
}

👥总结

本篇博文对 【从零实现高并发内存池】- 项目介绍、原理 及 内存池详解 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

相关文章:

【从零实现高并发内存池】- 项目介绍、原理 及 内存池详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

CSS margin(外边距)学习笔记

CSS 中的 margin 属性用于定义元素周围的空白区域&#xff0c;它是一个非常重要的布局工具&#xff0c;可以帮助我们控制元素之间的间距&#xff0c;从而实现更美观和易用的页面布局。以下是对 margin 属性的详细学习笔记。 一、margin 的基本概念 margin 是元素周围的透明区…...

【数据集】中国各省低空经济及无人机相关数据集(1996-2025年2月)

低空经济泛指3000米高空以下的飞行经济活动&#xff0c;以民用客运飞行器和无人驾驶航空器为主。低空经济产业是先进飞行器出行&#xff08;AAM&#xff09;在城市低空运行的一种变革性和颠覆性的复合新产业&#xff0c;主要以垂直起降型飞机&#xff08;VTOL&#xff09;与无人…...

C++动态分配内存知识点!

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 大家好呀&#xff0c;又是分享干货的时间&#xff0c;今天我们来学习一下动态分配内存。 文章目录 1.动态分配内存的思想 2.动态分配内存的概念 2.1内存分配函数 2.2动态内存的申请和释放 2.3内存碎片问…...

哈喽打车 小程序 分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向过程 这一次遇到这种风控感觉挺有…...

泛型的二三事

泛型&#xff08;Generics&#xff09;是Java语言的一个重要特性&#xff0c;它允许在定义类、接口和方法时使用类型参数&#xff08;Type Parameters&#xff09;&#xff0c;从而实现类型安全的代码重用。泛型在Java 5中被引入&#xff0c;极大地增强了代码的灵活性和安全性。…...

云计算:数字浪潮中的第三次文明跃迁——从虚拟化到智能协同的范式革命

一、浪潮的序曲&#xff1a;从机械革命到数字原子的觉醒 20世纪中叶&#xff0c;当晶体管的发明点燃信息革命的火种时&#xff0c;人类社会的第三次浪潮已悄然萌芽。托夫勒预言的“信息将成为新的权力核心”&#xff0c;在21世纪初以云计算的形态具象化。这场浪潮的起点&#…...

redis哨兵机制 和集群有什么区别:

主从&#xff1a; 包括一个master节点 和多个slave节点&#xff1a; master节点负责数据的读写&#xff0c;slave节点负责数据的读取&#xff0c;master节点收到数据变更&#xff0c;会同步到slave节点 去实现数据的同步。通过这样一个架构可以去实现redis的一个读写分离。提升…...

java基础2

构造器&#xff1a; 构造器与类同名&#xff1b; 每个类可以有一个以上的构造器&#xff1b; 构造器可以有0个&#xff0c;1个或多个参数&#xff1b; 构造器没有返回值&#xff1b; 构造器总是伴着new一起调用 方法重载&#xff1a; 方法名字一样&#xff0c;参数不一样…...

《算法笔记》3.6小节——入门模拟->字符串处理

1009 说反话 #include <cstdio>int main() {char sen[80][80];int num0;while(scanf("%s",sen[num])!EOF){num;}for (int i num-1; i > 0; --i) {printf("%s ",sen[i]);}printf("%s\n",sen[0]);return 0; }字符串连接 #include <io…...

JavaScript:BOM编程

今天我要介绍的是JS中有关于BOM编程的知识点内容&#xff1a;BOM编程&#xff1b; 介绍&#xff1a;BOM全名&#xff08;Browser Object Model&#xff08;浏览器对象模型&#xff09;&#xff09;。 是浏览器提供的与浏览器窗口交互的接口&#xff0c;其核心对象是 window。与…...

用户自定义函数(UDF)开发与应用(二)

五、UDF 在不同平台的应用 5.1 数据库中的 UDF 应用&#xff08;如 MySQL、PostgreSQL&#xff09; 在数据库领域&#xff0c;UDF 为开发者提供了强大的扩展能力&#xff0c;使得数据库可以完成一些原本内置函数无法实现的复杂操作。 以 MySQL 为例&#xff0c;假设我们有一…...

C++——继承、权限对继承的影响

目录 继承基本概念 编程示例 1.基类&#xff08;父类&#xff09;Person 代码特点说明 权限对类的影响 ​编辑 编程示例 1. 公有继承 (public inheritance) 2. 保护继承 (protected inheritance) 3. 私有继承 (private inheritance) 重要规则 实际应用 继承基本概…...

Tkinter样式与主题定制

在创建图形用户界面&#xff08;GUI&#xff09;应用时&#xff0c;除了功能的实现外&#xff0c;界面的外观和用户体验也非常重要。Tkinter提供了多种方式来定制控件的样式&#xff0c;使应用程序界面更加美观和易用。在这一章中&#xff0c;我们将介绍如何使用Tkinter的样式和…...

CSS 背景属性学习笔记

CSS 背景属性用于定义 HTML 元素的背景效果&#xff0c;包括背景颜色、背景图像、图像平铺方式、图像定位以及图像是否固定等。以下是关于 CSS 背景属性的详细学习笔记。 一、背景颜色&#xff08;background-color&#xff09; background-color 属性用于定义元素的背景颜色…...

信息安全管理与评估2023广东省样题答案截图视频

2023年广东省职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书 一、 赛项时间 9:00-13:30&#xff0c;共计4小时30分&#xff0c;含赛题发放、收卷时间。 二、 赛项内容 本次大赛&#xff0c;各位选手需要完成三个阶段的任务&#xff0c;其中第一个阶段需要…...

ubuntu学习day1

linux常用命令 1. 用户相关 1.1 切换用户 su root #切换到root用户 su user #切换到普通用户sudo能赋予普通用户管理者权限&#xff0c;一般不要直接使用root用户进行操作。 1.2 添加用户 useradd 用户名 useradd user1 #添加了用户名为user1的用户但在ubuntu中想要创建普…...

ubuntu22.04-VMware Workstation移动后无法连接网络

1.VMware 中查看NAT模式 2.查看宿主机VMnet8的IP地址 虚拟机里设置成192.168.20.160 &#xff0c; 255.255.255.0, 192.168.20.2 在ubuntu系统中设置如下&#xff1a; 至此可以连上了。...

如何评估大模型的性能?有哪些常用的评估指标?

评估大模型(如大语言模型 LLM)的性能是一个多维度的问题,常常需要结合多个指标从不同角度来考察模型的能力。以下是常见的评估方法和指标: 一、通用评估维度 任务性能(Task Performance) 衡量模型在特定任务上的表现,如问答、翻译、总结等。 语言能力(Linguistic Capa…...

Linux驱动开发-网络设备驱动

Linux驱动开发-网络设备驱动 一&#xff0c;网络设备总体结构1.1 总体架构1.2 NAPI数据处理机制 二&#xff0c;RMII和MDIO2.1 RMII接口2.2 MDIO接口 三&#xff0c;MAC和PHY模块3.1 MAC模块3.2 PHY模块 四&#xff0c;网络模型4.1 网络的OSI和TCP/IP分层模型4.1.1 传输层&…...

CTF web入门之文件包含

web78: include函数执行file引入的文件&#xff0c;如果执行不成功&#xff0c;就高亮显示当前页面的源码。 方法一&#xff1a;filter伪协议 file关键字的get参数传递&#xff0c;php://是一种协议名称&#xff0c;php://filter/是一种访问本地文件的协议&#xff0c;/readc…...

error: failed to run custom build command for `yeslogic-fontconfig-sys v6.0.0`

rust使用plotters时遇到编译错误。 一、错误 error: failed to run custom build command for yeslogic-fontconfig-sys v6.0.0 二、解决方法 我用的是opensuse&#xff0c;使用下面命令可以解决问题。 sudo zypper in fontconfig-devel...

低资源需求的大模型训练项目---调研0.5B大语言模型

一、主流0.5B大语言模型及性能对比 1. Qwen系列&#xff08;阿里&#xff09; • Qwen2.5-0.5B&#xff1a;阿里2024年9月开源的通义千问系列最小尺寸模型&#xff0c;支持32K上下文长度和8K生成长度。在中文场景下表现优异&#xff0c;指令跟踪、JSON结构化输出能力突出&…...

信息安全管理与评估广东省2023省赛正式赛题

任务1&#xff1a;网络平台搭建(60分) 题号 网络需求 1 根据网络拓扑图所示&#xff0c;按照IP地址参数表&#xff0c;对DCFW的名称、各接口IP地址进行配置。&#xff08;10分&#xff09; 2 根据网络拓扑图所示&#xff0c;按照IP地址参数表&#xff0c;对DCRS的名称进…...

LeetCode.225. 用队列实现栈

用队列实现栈 题目解题思路1. push2. pop3. empty CodeQueue.hQueue.cStack.c 题目 225. 用队列实现栈 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现…...

CTF--bp

一、原题&#xff1a; &#xff08;1&#xff09;提示&#xff1a;弱密码top1000&#xff1f;z????? &#xff08;2&#xff09;原网页&#xff1a; 二、步骤&#xff1a; 1.先打开BP&#xff0c;随便输入一个密码&#xff1a; 2.打开BP&#xff0c;发现password&#…...

01_背包问题

package org.josh; import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner new Scanner(System.in); int n scanner.nextInt(); // 物品数量 long w scanner.nextLong(); // 背包容量&#xff0c;使用long防止溢出 int[] v …...

ps 人像学习

视频&#xff1a; 一ps快捷键 1.1 创建图层 ctrlj 1.2 放大缩小图片的大小 按住alt 滚轮 1.3 移动图片 空格 左键 1.4 撤回 ctrlz 二 精修的第一步是去除斑点&#xff0c;瑕疵&#xff0c; 2.1 污点修复画笔工具 新建一个图层&#xff0c;点击污点修复工具进行修复…...

【AI论文】MM-IFEngine:迈向多模态指令遵循

摘要&#xff1a;指令遵循&#xff08;IF&#xff09;能力衡量多模态大语言模型&#xff08;MLLM&#xff09;准确理解用户告诉他们的内容以及他们是否做得正确的能力。 现有的多模态指令训练数据很少&#xff0c;基准测试简单&#xff0c;指令原子化&#xff0c;对于要求精确输…...

【C++初学】课后作业汇总复习(五) 单目运算符重载

本题主要考察-构造函数的定义和操作符重载、友元函数等 根据后缀和程序样例输出&#xff0c;完成分数类和相关函数的定义&#xff0c; 输入&#xff1a; -6 12 8 -16 输出&#xff1a; 1/2 1/1 -1/2 / -1/2 - -1/2 0/1 输入&#xff1a; 3 7 2 6 输出&#xff1a; 1/…...

Python基础语法速通(自用笔记)

目录 # 输出直接print就行了 # 次方&#xff0c;除法&#xff0c;取整 # 定义变量直接写就可以&#xff0c;不用写类型 # 基础的while不用写&#xff08;&#xff09;和{}&#xff0c;直接用冒号即可&#xff0c;缩进对齐 # 这里的for循环直接用in就可以,意思是从...中一个…...

Nginx基础讲解

Nginx基础讲解 Nginx 是一款高性能的 HTTP 服务器和反向代理服务器&#xff0c;广泛用于负载均衡、静态资源托管、SSL 终端等场景。以下是对 Nginx 的详细讲解&#xff1a; 1. Nginx 核心概念​ ​事件驱动架构​&#xff1a;基于异步非阻塞模型&#xff0c;高效处理高并发连接…...

K8S+Prometheus+Consul+alertWebhook实现全链路服务自动发现与监控、告警配置实战

系列文章目录 k8s服务注册到consul prometheus监控标签 文章目录 系列文章目录前言一、环境二、Prometheus部署1.下载2.部署3.验证 三、kube-prometheus添加自定义监控项1.准备yaml文件2.创建新的secret并应用到prometheus3.将yaml文件应用到集群4.重启prometheus-k8s pod5.访…...

组件安全工程化革命:从防御体系构建到安全基因重塑

文章目录 总起&#xff1a;数字世界的钢铁长城 分论&#xff1a; 一、组件生态的"七宗罪"与安全基因重组 二、百万级流量下的安全工程化实战 三、性能与安全的共生进化论 四、安全工程化全链路解决方案 总束&#xff1a;安全基因驱动的未来图景 五、时代思考…...

(PC+WAP)大气滚屏网站模板 电气电力设备网站源码下载

源码介绍 (PCWAP)大气滚屏网站模板 电气电力设备网站源码下载。PbootCMS内核开发的网站模板&#xff0c;该模板适用于滚屏网站模板、电气电力设备网站源码等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b;PCWAP&#xff0c…...

发送加密信息的简单实现【Java】

&#xff08;修改期&#xff09; 一、代码的引用处 public static SecretKeys generateKeys() throws NoSuchAlgorithmException {: 定义一个公共静态方法&#xff0c;用于生成 AES 和 HMAC 密钥对。 public static String encrypt(String plaintext, SecretKey aesKey, S…...

阿里云域名解析

一、打开域名控制台 PC端浏览器打开阿里云域名控制台:域名控制台,点击"域名解析"。 二、添加解析设置 选择需要解析的域名,点击"解析设置"。 点击"添加记录"。 添加@和www即可。...

DNS域名解析服务(正向 反向 主从)

DNS 1.分散式管理&#xff1a; Hosts文件 一改百度就不会访问了 Ip地址 域名 121&#xff0e;226.246.3 www.jd.com 2.我们会搭建一台 域名解析服务器全世界得域名全靠这台服务器进行解析 中央集权制 域名是由多个部分组成的 www.baidu.com .baidu .com是域…...

ROS2---std_msgs基础消息包

std_msgs 是ROS 2&#xff08;Robot Operating System 2&#xff09;里的基础消息包&#xff0c;它定义了一系列简单却常用的消息类型&#xff0c;为不同节点间的通信提供了基础的数据格式。 1. 消息包概述 std_msgs 包包含了多种基础消息类型&#xff0c;这些类型用于表示常…...

python基础:数据类型转换、运算符(算术运算符、比较运算符、逻辑运算符、三元运算符、位运算符)

目录 一、类型转换 隐式类型转换/自动转换&#xff1a; 显示类型转换/强制转换&#xff1a; 二、运算符 算数运算符&#xff1a; - * / 比较运算符 逻辑/布尔运算符 赋值运算符&#xff1a; 三元运算符 位运算符 [二进制] 运算符优先级 一、类型转换 python变量的类…...

[特殊字符] 终端效率提升指南:zsh + tmux

在日常开发中&#xff0c;一个舒适、高效的终端环境能显著提升工作效率。本文将介绍如何通过配置 oh-my-zsh 和 tmux 打造一个功能强大、便捷实用的终端工具集。无论你是 Linux 新手&#xff0c;还是资深开发者&#xff0c;都能从中获得实用的提升技巧。 &#x1f300; 一、终…...

【Linux篇】深入理解文件系统:从基础概念到 ext2 文件系统的应用与解析

文件系统的魔法&#xff1a;让计算机理解并存储你的数据 一. 文件系统1.1 块1.2 分区1.3 inode(索引节点) 二. ext2文件系统2.1 认识文件系统2.2 Block Group (块组)2.2.1 Block Group 的基本概念2.2.2 Block Group 的作用 2.3 块组内部结构2.3.1 超级块&#xff08;Super Bloc…...

MarkDown 输出表格的方法

MarkDown用来输出表格很简单&#xff0c;比Word手搓表格简单多了&#xff0c;而且方便修改。 MarkDown代码&#xff1a; |A|B|C|D| |:-|-:|:-:|-| |1|b|c|d| |2|b|c|d| |3|b|c|d| |4|b|c|d| |5|b|c|d|显示效果&#xff1a; ABCD1bcd2bcd3bcd4bcd5bcd A列强制左对齐&#xf…...

DOM解析XML:Java程序员的“乐高积木式“数据搭建

各位代码建筑师们&#xff01;今天我们要玩一个把XML变成内存乐高城堡的游戏——DOM解析&#xff01;和SAX那种"边看监控边破案"的刺激不同&#xff0c;DOM就像把整个乐高说明书一次性倒进大脑&#xff0c;然后慢慢拼装&#xff08;内存&#xff1a;你不要过来啊&…...

Python 数组里找出子超集

碰见一个问题&#xff0c;有一个大数组&#xff0c;如下所示&#xff1a; xx [[1, 3, 4], [3, 4, 5], [1, 2, 3, 4, 5], [6], [7, 8], [6, 7, 8]]大数组里面有好多小的数组&#xff0c;观察发现&#xff0c;小的数组其实有挺多别的小数组的子集&#xff0c;现在问题来了&…...

上层 Makefile 控制下层 Makefile ---- 第二部分(补充一些例子与细节)

1. 递归调用子目录 Makefile 通过 $(MAKE) -C 进入子目录并执行其 Makefile&#xff0c;这是最常见的分层构建方法。 示例&#xff1a;基本递归调用 目录结构&#xff1a; project/ ├── Makefile # 顶层 Makefile ├── lib/ │ ├── Makefile # 子目录…...

LeetCode算法题(Go语言实现)_44

题目 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份是一组直接或间接相连的城市&#xff0c;组内不含其他没有相连的城市。 给你…...

STM32 HAL库之USART示例代码

串口发送和接收以及回调函数都可在这个文件中查询&#xff1a;stm32f1xx_hal_uart.h 串口配置初始化代码main.c中&#xff1a;MX_USART1_UART_Init();&#xff0c;初始化 UART 高层参数&#xff08;波特率、数据位、停止位、校验、模式等&#xff09; void MX_USART1_UART_In…...

头歌educoder——数据库 第10-11章

第10章 1、 事务的&#xff08; &#xff09;特性要求事务必须被视为一个不可分割的最小工作单元 A、 原子性 B、 一致性 C、 隔离性 D、 持久性 2、 事务的&#xff08; &#xff09;特性要求一个事务在执行时&#xff0c;不会受到其他事务的影响。 A、 原子性 B、 一致性 C…...

从 Vue 到 React:深入理解 useState 的异步更新与函数式写法

目录 从 Vue 到 React&#xff1a;深入理解 useState 的异步更新与函数式写法1. Vue 的响应式回顾&#xff1a;每次赋值立即生效2. React 的状态更新是异步且批量的原因解析 3. 函数式更新&#xff1a;唯一的正确写法4. 对比 Vue vs React 状态更新5. React useState 的核心源码…...