带你用纯C实现一个内存池(图文结合)
为什么要用内存池
为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,非常容易出现内存泄漏导致mmo的问题。
为了解决这两个问题,内存池就应运而生了。内存池预先分配一大块内存来做一个内存池,业务中的内存分配和释放都由这个内存池来管理,内存池内的内存不足时其内部会自己申请。所以内存碎片的问题就交由内存池的算法来优化,而内存泄漏的问题只需要遵守内存池提供的api,就非常容易避免内存泄漏了。
即使出现了内存泄漏,排查的思路也很清晰。1.检查是不是内存池的问题;2.如果不是内存池的问题,就检查是不是第三方库的内存泄漏。
内存池的使用场景
- 全局内存池
- 一个连接一个内存池(本文实现这个场景的内存池)
设计一个内存池
总体介绍
由于本文是一个连接一个内存池,所以后续介绍和代码都是以4k为分界线,大于4k的我们认为是大块内存;小于4k的我们认为是小块内存。并且注意这里的4k,并不是严格遵照4096,而是在描述上,用4k比较好描述。
在真正使用内存之前,内存池提前分配一定数量且大小相等的内存块以作备用,当真正被用户调用api分配内存的时候,直接从内存块中获取内存(指小块内存),当内存块不够用了,再有内存池取申请新的内存块。而如果是需要大块内存,则内存池直接申请大块内存再返回给用户。
内存池:就是将这些提前申请的内存块组织管理起来的数据结构,内存池实现原理主要分为分配,回收,扩容三部分。
内存池原理之小块内存:分配=> 内存池预申请一块4k的内存块,这里称为block,即block=4k内存块。当用户向内存池申请内存size小于4k时,内存池从block的空间中划分出去size空间,当再有新申请时,再划分出去。扩容=> 直到block中的剩余空间不足以分配size大小,那么此时内存池会再次申请一块block,再从新的block中划分size空间给用户。回收=> 每一次申请小内存,都会在对应的block中引用计数加1,每一次释放小内存时,都会在block中引用计数减1,只有当引用计数为零的时候,才会回收block使他重新成为空闲空间,以便重复利用空间。这样,内存池避免频繁向内核申请/释放内存,从而提高系统性能。
内存池原理之大块内存:分配=> 因为大块内存是大于4k的,所以内存池不预先申请内存,也就是用户申请的时候,内存池再申请内存,然后返回给用户。扩容=> 大块内存不存在扩容。回收=> 对于大块内存来说,回收就直接free掉即可。
上面理论讲完了,下面来介绍如何管理小块内存和大块内存。
小块内存的分配与管理
在创建内存池的时候,会预先申请一块4k的内存,并且在起始处将pool的结构体和node的结构体放进去,从last开始一直到end都是空闲内存,<last , end >中间的区域就用来存储小块内存。每一次mp_malloc,就将last指针后移,直到 e n d − l a s t < s i z e end - last < size end−last<size 时,进行扩容,将新block的last后移即可。
初始状态
分配内存
扩容
相关视频推荐
200行代码实现slab,开启内存池的内存管理(准备linux环境)
线程池、内存池、异步请求池、数据库连接池、无锁队列的ringbuffer,提升程序性能必备技术
5种内存泄漏检测方式,让你重新理解C++内存管理
免费学习地址:C/C++Linux服务器开发/后台架构师
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
大块内存的分配与管理
对于大块内存,前面已经说了,用户申请的时候,内存池才申请
申请一块大内存
再申请一块大内存
内存池代码实现
向外提供的api
- mp_create_pool:创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
- mp_destroy_pool:销毁内存池,遍历小块结构体和大块结构体,进行free释放内存
- mp_malloc:提供给用户申请内存的api
- mp_calloc:通过mp_malloc申请内存后置零,相当于calloc
- mp_free:释放由mp_malloc返回的内存
- mp_reset_pool:将block的last置为初始状态,销毁所有大块内存
- monitor_mp_poll:监控内存池状态
struct mp_pool_s *mp_create_pool(size_t size);void mp_destroy_pool(struct mp_pool_s *pool);void *mp_malloc(struct mp_pool_s *pool, size_t size);void *mp_calloc(struct mp_pool_s *pool, size_t size);void mp_free(struct mp_pool_s *pool, void *p);void mp_reset_pool(struct mp_pool_s *pool);void monitor_mp_poll(struct mp_pool_s *pool, char *tk);
相关结构体的定义
mp_pool_s 就是整个内存池的管理结构,我们做的内存池是一个连接一个内存池,所以对于整个程序而言,内存池对象是有很多个的。
可能读者会有疑问,有了head,为什么还有current,是因为如果一个block剩余空间小于size超过一定次数后,将current指向下一个block,这样就加快内存分配效率,减少遍历次数。
//每4k一block结点
struct mp_node_s {unsigned char *end;//块的结尾unsigned char *last;//使用到哪了struct mp_node_s *next;//链表int quote;//引用计数int failed;//失效次数
};struct mp_large_s {struct mp_large_s *next;//链表int size;//alloc的大小void *alloc;//大块内存的起始地址
};struct mp_pool_s {struct mp_large_s *large;struct mp_node_s *head;struct mp_node_s *current;
};
内存对齐
访问速度是内存对齐的原因之一,另外一个原因是某些平台(arm)不支持未内存对齐的访问
在4k里面划分内存,那么必然有很多地方是不对齐的,所以这里提供两个内存对齐的函数。那么为什么要内存对齐呢?其一:提高访问速度;其二:某些平台arm不支持未对其的内存访问,会出错。
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
创建与销毁内存池
创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
销毁内存池,遍历小块结构体和大块结构体,进行free释放内存。
//创建内存池
struct mp_pool_s *mp_create_pool(size_t size) {struct mp_pool_s *pool;if (size < PAGE_SIZE || size % PAGE_SIZE != 0) {size = PAGE_SIZE;}//分配4k以上不用malloc,用posix_memalign/*int posix_memalign (void **memptr, size_t alignment, size_t size);*/int ret = posix_memalign((void **) &pool, MP_ALIGNMENT, size); //4K + mp_pool_sif (ret) {return NULL;}pool->large = NULL;pool->current = pool->head = (unsigned char *) pool + sizeof(struct mp_pool_s);pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);pool->head->end = (unsigned char *) pool + PAGE_SIZE;pool->head->failed = 0;return pool;
}//销毁内存池
void mp_destroy_pool(struct mp_pool_s *pool) {struct mp_large_s *large;for (large = pool->large; large; large = large->next) {if (large->alloc) {free(large->alloc);}}struct mp_node_s *cur, *next;cur = pool->head->next;while (cur) {next = cur->next;free(cur);cur = next;}free(pool);
}
提供给用户的内存申请api
申请的内存以size做区分,如果大于4k就分配大块内存,小于4k就去block里面划分。
//分配内存
void *mp_malloc(struct mp_pool_s *pool, size_t size) {if (size <= 0) {return NULL;}if (size > PAGE_SIZE - sizeof(struct mp_node_s)) {//largereturn mp_malloc_large(pool, size);}else {//smallunsigned char *mem_addr = NULL;struct mp_node_s *cur = NULL;cur = pool->current;while (cur) {mem_addr = mp_align_ptr(cur->last, MP_ALIGNMENT);if (cur->end - mem_addr >= size) {cur->quote++;//引用+1cur->last = mem_addr + size;return mem_addr;}else {cur = cur->next;}}return mp_malloc_block(pool, size);// open new space}
}
void *mp_calloc(struct mp_pool_s *pool, size_t size) {void *mem_addr = mp_malloc(pool, size);if (mem_addr) {memset(mem_addr, 0, size);}return mem_addr;
}
小块内存block扩容
所有的block都 e n d − l a s t < s i z e end - last < size end−last<size 时,进行扩容,将新block的last后移即可。
//new block 4k
void *mp_malloc_block(struct mp_pool_s *pool, size_t size) {unsigned char *block;int ret = posix_memalign((void **) &block, MP_ALIGNMENT, PAGE_SIZE); //4Kif (ret) {return NULL;}struct mp_node_s *new_node = (struct mp_node_s *) block;new_node->end = block + PAGE_SIZE;new_node->next = NULL;unsigned char *ret_addr = mp_align_ptr(block + sizeof(struct mp_node_s), MP_ALIGNMENT);new_node->last = ret_addr + size;new_node->quote++;struct mp_node_s *current = pool->current;struct mp_node_s *cur = NULL;for (cur = current; cur->next; cur = cur->next) {if (cur->failed++ > 4) {current = cur->next;}}//now cur = last nodecur->next = new_node;pool->current = current;return ret_addr;
}
分配大块内存
//size>4k
void *mp_malloc_large(struct mp_pool_s *pool, size_t size) {unsigned char *big_addr;int ret = posix_memalign((void **) &big_addr, MP_ALIGNMENT, size); //sizeif (ret) {return NULL;}struct mp_large_s *large;//released struct large resumeint n = 0;for (large = pool->large; large; large = large->next) {if (large->alloc == NULL) {large->size = size;large->alloc = big_addr;return big_addr;}if (n++ > 3) {break;// 为了避免过多的遍历,限制次数}}large = mp_malloc(pool, sizeof(struct mp_large_s));if (large == NULL) {free(big_addr);return NULL;}large->size = size;large->alloc = big_addr;large->next = pool->large;pool->large = large;return big_addr;
}
释放内存
如果是大块内存,找到之后直接释放;如果是小块内存,将引用计数减1,如果引用计数为0则重置last。
//释放内存
void mp_free(struct mp_pool_s *pool, void *p) {struct mp_large_s *large;for (large = pool->large; large; large = large->next) {//大块if (p == large->alloc) {free(large->alloc);large->size = 0;large->alloc = NULL;return;}}//小块 引用-1struct mp_node_s *cur = NULL;for (cur = pool->head; cur; cur = cur->next) {
// printf("cur:%p p:%p end:%p\n", (unsigned char *) cur, (unsigned char *) p, (unsigned char *) cur->end);if ((unsigned char *) cur <= (unsigned char *) p && (unsigned char *) p <= (unsigned char *) cur->end) {cur->quote--;if (cur->quote == 0) {if (cur == pool->head) {pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);}else {cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);}cur->failed = 0;pool->current = pool->head;}return;}}
}
内存池测试
//
// Created by 68725 on 2022/7/26.
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>#define PAGE_SIZE 4096
#define MP_ALIGNMENT 16
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))//每4k一block结点
struct mp_node_s {unsigned char *end;//块的结尾unsigned char *last;//使用到哪了struct mp_node_s *next;//链表int quote;//引用计数int failed;//失效次数
};struct mp_large_s {struct mp_large_s *next;//链表int size;//alloc的大小void *alloc;//大块内存的起始地址
};struct mp_pool_s {struct mp_large_s *large;struct mp_node_s *head;struct mp_node_s *current;
};struct mp_pool_s *mp_create_pool(size_t size);void mp_destroy_pool(struct mp_pool_s *pool);void *mp_malloc(struct mp_pool_s *pool, size_t size);void *mp_calloc(struct mp_pool_s *pool, size_t size);void mp_free(struct mp_pool_s *pool, void *p);void mp_reset_pool(struct mp_pool_s *pool);void monitor_mp_poll(struct mp_pool_s *pool, char *tk);void mp_reset_pool(struct mp_pool_s *pool) {struct mp_node_s *cur;struct mp_large_s *large;for (large = pool->large; large; large = large->next) {if (large->alloc) {free(large->alloc);}}pool->large = NULL;pool->current = pool->head;for (cur = pool->head; cur; cur = cur->next) {cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);cur->failed = 0;cur->quote = 0;}
}//创建内存池
struct mp_pool_s *mp_create_pool(size_t size) {struct mp_pool_s *pool;if (size < PAGE_SIZE || size % PAGE_SIZE != 0) {size = PAGE_SIZE;}//分配4k以上不用malloc,用posix_memalign/*int posix_memalign (void **memptr, size_t alignment, size_t size);*/int ret = posix_memalign((void **) &pool, MP_ALIGNMENT, size); //4K + mp_pool_sif (ret) {return NULL;}pool->large = NULL;pool->current = pool->head = (unsigned char *) pool + sizeof(struct mp_pool_s);pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);pool->head->end = (unsigned char *) pool + PAGE_SIZE;pool->head->failed = 0;return pool;
}//销毁内存池
void mp_destroy_pool(struct mp_pool_s *pool) {struct mp_large_s *large;for (large = pool->large; large; large = large->next) {if (large->alloc) {free(large->alloc);}}struct mp_node_s *cur, *next;cur = pool->head->next;while (cur) {next = cur->next;free(cur);cur = next;}free(pool);
}//size>4k
void *mp_malloc_large(struct mp_pool_s *pool, size_t size) {unsigned char *big_addr;int ret = posix_memalign((void **) &big_addr, MP_ALIGNMENT, size); //sizeif (ret) {return NULL;}struct mp_large_s *large;//released struct large resumeint n = 0;for (large = pool->large; large; large = large->next) {if (large->alloc == NULL) {large->size = size;large->alloc = big_addr;return big_addr;}if (n++ > 3) {break;// 为了避免过多的遍历,限制次数}}large = mp_malloc(pool, sizeof(struct mp_large_s));if (large == NULL) {free(big_addr);return NULL;}large->size = size;large->alloc = big_addr;large->next = pool->large;pool->large = large;return big_addr;
}//new block 4k
void *mp_malloc_block(struct mp_pool_s *pool, size_t size) {unsigned char *block;int ret = posix_memalign((void **) &block, MP_ALIGNMENT, PAGE_SIZE); //4Kif (ret) {return NULL;}struct mp_node_s *new_node = (struct mp_node_s *) block;new_node->end = block + PAGE_SIZE;new_node->next = NULL;unsigned char *ret_addr = mp_align_ptr(block + sizeof(struct mp_node_s), MP_ALIGNMENT);new_node->last = ret_addr + size;new_node->quote++;struct mp_node_s *current = pool->current;struct mp_node_s *cur = NULL;for (cur = current; cur->next; cur = cur->next) {if (cur->failed++ > 4) {current = cur->next;}}//now cur = last nodecur->next = new_node;pool->current = current;return ret_addr;
}//分配内存
void *mp_malloc(struct mp_pool_s *pool, size_t size) {if (size <= 0) {return NULL;}if (size > PAGE_SIZE - sizeof(struct mp_node_s)) {//largereturn mp_malloc_large(pool, size);}else {//smallunsigned char *mem_addr = NULL;struct mp_node_s *cur = NULL;cur = pool->current;while (cur) {mem_addr = mp_align_ptr(cur->last, MP_ALIGNMENT);if (cur->end - mem_addr >= size) {cur->quote++;//引用+1cur->last = mem_addr + size;return mem_addr;}else {cur = cur->next;}}return mp_malloc_block(pool, size);// open new space}
}void *mp_calloc(struct mp_pool_s *pool, size_t size) {void *mem_addr = mp_malloc(pool, size);if (mem_addr) {memset(mem_addr, 0, size);}return mem_addr;
}//释放内存
void mp_free(struct mp_pool_s *pool, void *p) {struct mp_large_s *large;for (large = pool->large; large; large = large->next) {//大块if (p == large->alloc) {free(large->alloc);large->size = 0;large->alloc = NULL;return;}}//小块 引用-1struct mp_node_s *cur = NULL;for (cur = pool->head; cur; cur = cur->next) {
// printf("cur:%p p:%p end:%p\n", (unsigned char *) cur, (unsigned char *) p, (unsigned char *) cur->end);if ((unsigned char *) cur <= (unsigned char *) p && (unsigned char *) p <= (unsigned char *) cur->end) {cur->quote--;if (cur->quote == 0) {if (cur == pool->head) {pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);}else {cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);}cur->failed = 0;pool->current = pool->head;}return;}}
}void monitor_mp_poll(struct mp_pool_s *pool, char *tk) {printf("\r\n\r\n------start monitor poll------%s\r\n\r\n", tk);struct mp_node_s *head = NULL;int i = 0;for (head = pool->head; head; head = head->next) {i++;if (pool->current == head) {printf("current==>第%d块\n", i);}if (i == 1) {printf("第%02d块small block 已使用:%4ld 剩余空间:%4ld 引用:%4d failed:%4d\n", i,(unsigned char *) head->last - (unsigned char *) pool,head->end - head->last, head->quote, head->failed);}else {printf("第%02d块small block 已使用:%4ld 剩余空间:%4ld 引用:%4d failed:%4d\n", i,(unsigned char *) head->last - (unsigned char *) head,head->end - head->last, head->quote, head->failed);}}struct mp_large_s *large;i = 0;for (large = pool->large; large; large = large->next) {i++;if (large->alloc != NULL) {printf("第%d块large block size=%d\n", i, large->size);}}printf("\r\n\r\n------stop monitor poll------\r\n\r\n");
}int main() {struct mp_pool_s *p = mp_create_pool(PAGE_SIZE);monitor_mp_poll(p, "create memory pool");
#if 0printf("mp_align(5, %d): %d, mp_align(17, %d): %d\n", MP_ALIGNMENT, mp_align(5, MP_ALIGNMENT), MP_ALIGNMENT,mp_align(17, MP_ALIGNMENT));printf("mp_align_ptr(p->current, %d): %p, p->current: %p\n", MP_ALIGNMENT, mp_align_ptr(p->current, MP_ALIGNMENT),p->current);
#endifvoid *mp[30];int i;for (i = 0; i < 30; i++) {mp[i] = mp_malloc(p, 512);}monitor_mp_poll(p, "申请512字节30个");for (i = 0; i < 30; i++) {mp_free(p, mp[i]);}monitor_mp_poll(p, "销毁512字节30个");int j;for (i = 0; i < 50; i++) {char *pp = mp_calloc(p, 32);for (j = 0; j < 32; j++) {if (pp[j]) {printf("calloc wrong\n");exit(-1);}}}monitor_mp_poll(p, "申请32字节50个");for (i = 0; i < 50; i++) {char *pp = mp_malloc(p, 3);}monitor_mp_poll(p, "申请3字节50个");void *pp[10];for (i = 0; i < 10; i++) {pp[i] = mp_malloc(p, 5120);}monitor_mp_poll(p, "申请大内存5120字节10个");for (i = 0; i < 10; i++) {mp_free(p, pp[i]);}monitor_mp_poll(p, "销毁大内存5120字节10个");mp_reset_pool(p);monitor_mp_poll(p, "reset pool");for (i = 0; i < 100; i++) {void *s = mp_malloc(p, 256);}monitor_mp_poll(p, "申请256字节100个");mp_destroy_pool(p);return 0;
}
nginx内存池对比分析
相关结构体定义对比
创建内存池对比
内存申请对比
相关文章:

带你用纯C实现一个内存池(图文结合)
为什么要用内存池 为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,…...

ChatGPT使用案例之图像生成
ChatGPT使用案例之图像生成 这里一节我们介绍一下ChatGPT的图像生成,这里我们使用代码来完成,也就是通过API 来完成,因为ChatGPT 本身是不能生成图片的,言外之意我们图片生成是ChatGPT通过其他方式生成的 Images API提供了三种与…...

蚁群算法优化旅行问题
%%%%%%%%%%%%蚁群算法解决 TSP 问题%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%初始化%%%%%%%%%%%%%%%%%%% clear all; %清除所有变量 close all; %清图 clc; %清屏 m 50; %蚂蚁个数 Alpha 1; %信息素重要程度参数 Beta 5; %启发式因子重要程度参数 Rho 0.1; %信息素蒸发系数 G 20…...

树数据结构
什么是树数据结构? 树数据结构是一种层次结构,用于以易于导航和搜索的方式表示和组织数据。它是由边连接的节点集合,节点之间具有层次关系。树的最顶端的节点称为根,它下面的节点称为子节点。每个节点可以有多个子节点,…...

Spring Boot整合Redis并提供多种实际场景的应用
Spring Boot整合Redis并提供多种实际场景的应用1. 整合Redis2. 场景应用2.1 缓存2.2 分布式锁2.3 计数器2.4 发布/订阅3. 总结Spring Boot是一个快速构建基于Spring框架的应用程序的工具,它提供了大量的自动化配置选项,可以轻松地集成各种不同的技术。Re…...

VR全景图片,助力VR全景制作,720全景效果图
VR全景图片是指通过全景相机或多相机组合拍摄全景画面,并进行拼接处理生成全景图像的过程。VR全景图片的应用范围广泛,包括旅游和景区、房地产、汽车、艺术和文化、电影和娱乐等领域。本文将详细介绍VR全景图片的类型、应用场景、市场前景和发展趋势。 一…...

Kali Linux20款重要软件
Kali Linux 是一个流行的网络安全测试平台,它包含了大量的工具和应用程序,以下是其中20款最常用的软件和工具: Metasploit:Metasploit 是一个广泛使用的漏洞评估工具,可以帮助安全专业人员测试系统中的漏洞。Aircrack…...

C语言测试五
windows是什么类型的系统(实时还是分时)?有什么区别? 分时操作系统。如果在单核的情况下,分时操作系统多个进程共用一个单核,该单核会将其执行时间分成相应的时间片,每个进程占用一定的时间片&a…...

【微服务~原始真解】Spring Cloud —— 访问数据库整合Druid数据源
🔎这里是【秒懂云原生】,关注我学习云原生不迷路 👍如果对你有帮助,给博主一个免费的点赞以示鼓励 欢迎各位🔎点赞👍评论收藏⭐️ 👀专栏介绍 【秒懂云原生】 目前主要更新微服务,…...

前端入门必刷题,经典算法—两数之和
优美的前⾔ 年轻的码农哟~ 你是不是⼀直在思考⾃我提升的问题~ 思来想去,决定从算法抓起(单押)~ 拿起⼜放下,经历过多少次放弃(单押 ✖ 2)~ 决定了!这次让我来帮你梳理(单押 ✖ 3&a…...

‘海外/国外‘地区微博签到shu据(正题在第二部分)
最近失眠,研究了项关于weibo爬虫的新功能,种种原因,大家可跳过第一部分的引用直接看第二部分。 内容来源:健康中国、生命时报、央视等 失眠标准一:3个“30分钟” ● 入睡困难,从躺下想睡到睡着间隔…...

Springboot——SB整合Mybatis的CURD(基于注解进行开发)
此处是根据需求实现基本操作 上面这里涉及到了条件分页查询,还有增加和批量删除员工信息,右边编辑就是先查询后更新操作,叫做查询回显,然后在原有基础上进行更新 环境准备 在下面的入门案例的整体环境下把数据库表换成empSpring…...

现在大专生转IT可行吗?
当然可行的。 大专也是人,为什么不可以选择喜欢的专业学习,现在大学生遍地都是,学历已经不是限制你发展的因素了。有的人就是不擅长理论学习,更喜欢技术。IT也只是一个普普通通的技术行业,跟其他技术行业一样…...

XC7A50T-1CSG324I、XC7A50T-2CSG324I Artix-7 FPGA可编程门阵列
Artix-7 FPGA能够在多个方面实现更高的性价比,这些方面包括逻辑、信号处理、嵌入式内存、LVDS I/O、内存接口,以及收发器。MicroBlaze CPU针对Xilinx FPGA进行了优化,是一种可高度配置的32位RISC处理器,可为微控制器、实时处理器和…...

linux安装图片处理软件ImageMagick
下载地址: wget https://download.imagemagick.org/archive/ImageMagick-7.1.1-4.tar.gz 或者 wget --no-check-certificate https://download.imagemagick.org/archive/ImageMagick-7.1.1-4.tar.gz 安装命令: tar -zxvf ImageMagick-7.1.1-4.tar.…...

【Java基础】JavaCore核心-反射技术
文章目录1.什么是反射技术2.反射-获取类对象方式3.反射-获取声明构造器4.反射-对象创建实战5.反射-方法和属性实战6.反射-属性值操作实战7.反射-invoke运行类方法1.什么是反射技术 Java的反射(reflection)机制是指在程序的运行状态中 可以构造任意一个类…...

AWGN后验估计下的均值与协方差关系(向量和标量形式)
文章目录AWGN信道向量模型后验均值与协方差的关系从实数域拓展到复数域小结AWGN信道向量模型 考虑一个随机向量x∼pX(x)\boldsymbol x \sim p_{\boldsymbol X}(\boldsymbol x)x∼pX(x),信道模型为 qxv,v∼N(0,Σ)\boldsymbol q \boldsymbol x \boldsymbol v, \…...

Linux常用命令之文件搜索命令
1、常用搜索-find 命令find英文原意find所在路径/bin/find执行权限所有用户功能描述文件搜索语法find [搜索范围] [搜索条件] (默认准确搜索)范例find /etc -name init?? 常用的搜索条件的选项包括: -name:按照文件名进行匹配查找,例&…...

ChatGPT给软件测试行业带来的可能
软件测试在软件开发过程中扮演着至关重要的角色,因为它可以确保软件的质量和可靠性。而随着人工智能技术的不断发展,ChatGPT作为一个强大的自然语言处理工具,可以在软件测试中发挥出许多重要的作用。本文将介绍ChatGPT在软件测试应用中带来的…...

Cadence Allegro 导出Properties on Nets Report报告详解
⏪《上一篇》 🏡《上级目录》 ⏩《下一篇》 目录 1,概述2,Properties on Nets Report作用3,Properties on Nets Report示例4,Properties on Nets Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...

JAVA代码 实现定位数据动态聚集并绘制多边形区域
文章目录思路1、限制聚合距离2、绘制多边形区域3、多边形区域之间合并4、多边形定边点4、逻辑流程一些性能上的优化1、多边形设置圆心2、采用分支合并思路3、清理聚集较分散区域合理性处理1、解决多边形内凹角问题2、解决定边点插入位置问题3、多边形区域扩展成果展示最近有根据…...

基于储能进行调峰和频率调节研究【超线性增益的联合优化】(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

体验 Linux 的几个监控命令(htop、nmon、netdata)
体验 Linux 的几个监控命令htopnmonnetdatahtop 安装, sudo dnf install -y htop使用, htopnmon 安装, sudo dnf install -y nmon使用, nmon输入c, 输入C, 输入m, 输入n, 输入…...

NOC大赛2022NOC软件创意编程初赛图形化小低组(小学高年级组)
一、选择题 1.如果要控制所有角色一起朝舞台区右侧移动,下面哪个积太块是不需要的 2.要想让三个角色一起移动起来,下面哪个积木块没有作用 ? 3.小猴按照下面的程序前进,小猴最后一次前进了()步。 4.小可同学写了一个画笔程序画出花朵,但是运行后什么都看不到,不可…...

python进行股票收益率计算和风险控制的实现
股票收益率计算和风险控制的实现 在进行股票投资时,计算收益率和进行风险控制是非常重要的。本文将介绍一个与此相关的函数:radio_day_cal()。 radio_day_cal()函数 def radio_day_cal(last_day, sheet_name, df_dict, code_list, new_list):i 0days…...

自从有了这套近4000页的开发文档后,Java面试路上就像开了挂一样
Java是世界最流行的编程语言,也是国内大多数IT公司的主流语言。招聘网站上Java岗位众多,Java工程师似乎不愁找工作。但仔细一看就会发现,Java岗位的招聘薪酬天差地别,人才要求也是五花八门。而在Java工程师求职过程中,…...

Python文件操作
目录 一、文件操作介绍 二、文件的打开和关闭 三、文件的读写 四、文件文件夹相关操作 五、test 一、文件操作介绍 文件 : python中文件是对象 Liunx 文件 : 一切设备都可以看成是文件 磁盘文件 管道 网络Socket 文件属性: 读 写 执行权限 就是把一些存储存放起来&…...

036:cesium加载GPX文件,显示图形
第036个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载GPX文件, 显示图形。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共83行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https:/…...

【AI探索】我问了ChatGPT几个终极问题
终于尝试了一把ChatGPT的强大之处,问了一下关心的几个问题: chatGPT现在在思考吗?有没有什么你感兴趣的问题? 你认为AI会对人类产生哪些方面的影响? 你对人类所涉及到的学科有了解吗?你认为在哪些方面与人类…...

Leetcode 优先队列详解
优先队列 优先队列(Priority Queue):一种特殊的队列。在优先队列中,元素被赋予优先级,当访问队列元素时,具有最高优先级的元素最先删除 普通队列详解Leetcode 队列详解 优先队列与普通队列最大的不同点在于…...