【STM32】TCP/IP通信协议(2)--LwIP内存管理

五、LWIP内存管理

1.什么是内存管理?

(1)内存管理,是指软件运行时对计算机内存资源的分配的使用的技术,其主要目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源(就比如C语言当中的malloc 、free分配和释放)

内存分配:大数组,完成之后返回内存地址

内存释放:传入内存地址让算法进行释放

(2)LWIP内存管理策略

1.内存堆:提供合适大小的内存,剩余内存返回堆中

2.内存池:只能申请固定大小的内存,能有效防止内存碎片

3.C库: C运行时库自带的内存分配策略(不建议使用)

lwip内存池和内存堆本质上直接操作数组实现

(3)lwip内存堆和内存池的应用

  • 接收数据:MAC内核数组【内存堆和内存池可适用,正点原子阿波罗版本的使用的是内存池】

  • 发送数据:用户调用lwip的API接口【lwip一般选用内存堆申请内存】

  • 用户调用:可调用lwip的内存池和内存堆API接口申请内存

  • 接口控制块:netconn、socket、raw接口

  • 构建消息:API消息、数据包消息

2.lwip内存堆简介

lwip内存堆是一种可变长的分配策略,可以随意申请任意大小的内存,lwip内存堆采用的是First Fit(首次拟合)内存算法

(1)First Fit算法

从低地址空间往高地址空间查找,从中切割成合适的块,并把剩余的部分返回到动态内存堆中。

优点:

  • 内存浪费小、比较简单、适合小内存管理

  • 确保高地址空间具有足够的内存

  • 要求分配最小值以及相邻空间块合并,有效防止内存碎片

缺点:

  • 分配与释放频繁,会造成内存碎片

  • 分配和释放时,从低地址开始寻找,会导致效率慢

3.lwip内存堆原理解析

1

通过开辟一个内存堆,使用模拟IC运行时库的内存分配策略实现【大数组】

(1)管理内存块的结构体:

如下源码所示:

struct mem {mem_size_t next;      //指向下一个节点索引mem_size_t prev;      //指向上一个节点索引u8_t used;            //描述内存块是否可用    0:未使用   1:已使用
};
(2)最小内存分配
#ifndef MIN_SIZE
#define MIN_SIZE             12//最小分配内存
#endif /* MIN_SIZE */

为了防止内存碎片,lwip内核定义了最小分配大小MIN_SIZE。当用户申请的内存小于最小分配内存时,系统将分配MIN_SIZE大小的内存,

(3)对齐定义
//对齐操作
#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)//最小分配内存大小对齐---12字节
#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))//内存控制块对齐---8字节
#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)//内存堆对齐

SIZEOF_STRUCT_MEM宏定义用于确保内存大小进行4字节对齐,这不仅可以大大提升CPU的访问速度还因为某些硬件平台只能从特定地址处获取特定类型的数据。

(4)定义内存堆的空间
#ifndef LWIP_RAM_HEAP_POINTER 
​
/*定义堆内存空间*/
​
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM)); 
​
#define LWIP_RAM_HEAP_POINTER ram_heap 
​
#endif

无论是内存池还是内存堆都是对一个大数组进行操作。这种大数组被称为ram_heap数组,其大小常被定义为 MEM_SIZE_ALIGNED + (2U*SIZEOF_STR UCT_MEM)。这个能让用户申请内存时在这个大数组中分配相应大小的内存块,减少内存碎片。

(5)操作内存堆变量
/* 指向对齐后的内存堆的地址*/
static u8_t *ram;
/* 指向对齐后的内存堆的最后一个内存块 */
static struct mem *ram_end; 
/* 指向已被释放的索引号最小的内存块(内存堆最前面的已被释放的)*/
static struct mem * LWIP_MEM_LFREE_VOLATILE lfree;
​

lwIP 内核使用三个指针:ram 指针、ram_end 指针和 lfree 指针。

ram 指针指向ram_heap数组对齐后的内存堆总空间首地址

ram_end 指针指向ram_heap数组内存堆总空间尾地址(接近总空间的尾 地址)

lfree 指针指向ram_heap数组最低内存地址的空闲内存块。

在内存分配当中lwip根据lfree指针指向的空闲内存进行分配,从而快速找到可用的内存块,并有效分配内存给需要的任务。ram_end指针用于检测总内存堆空间中是否存在空闲的内存,以便进行进一步的内存分配。

(6)内存初始化mem_init():

下图是对堆空间进行初始化,初始化后的lfree指针指向第一个内存块,该内存块由控制块可用内存所组成,ram_end指针指向堆空间的尾部,用于判断堆空间是否存在可用内存,若lfree指针指向ram_end指针,则表示该堆空间没有可用内存进行分配

控制块:标记内存是否可用

可用内存:实际可分配的内存区域

void mem_init(void)
{struct mem *mem;
​LWIP_ASSERT("Sanity check alignment",(SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
​/* 对内存堆的地址(全局变量的名)进行对齐指向 ram_heap*/ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);/* 建立第一个内存块,内存块由内存块头+空间组成。 */mem = (struct mem *)(void *)ram;//附加在每个内存块前面的结构体/* 下一个内存块不存在,因此指向内存堆的结束 */mem->next = MEM_SIZE_ALIGNED;/* 前一个内存块就是它自己,因为这是第一个内存块 */ mem->prev = 0;/* 第一个内存块没有被使用 */ mem->used = 0;/* 初始化堆的末端(指向 MEM_SIZE_ALIGNED 底部位置)*/ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];/* 最后一个内存块被使用。因为其后面没有可用空间,必须标记为已被使用 */ram_end->used = 1;/* 下一个不存在,因此指向内存堆的结束 */ram_end->next = MEM_SIZE_ALIGNED;/* 前一个不存在,因此指向内存堆的结束 */ram_end->prev = MEM_SIZE_ALIGNED;
​/* 已释放的索引最小的内存块就是上面建立的第一个内存块。*/lfree = (struct mem *)(void *)ram;
​MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);/* 这里建立一个互斥信号量,主要是用来进行内存的申请、释放的保护 */if(sys_mutex_new(&mem_mutex) != ERR_OK) {LWIP_ASSERT("failed to create mem_mutex", 0);}
}

在内存分配过程中,lfree指针从低地址开始不短查找和划分内存,直到ram_end指针所指向的地址,这意味着lfree指针从堆空间的起始位置开始,逐个遍历内存块,直到找到可用的内存或到达堆空间的末尾才能结束分配。

(7)mem_malloc ():
void *mem_malloc(mem_size_t size_in){mem_size_t ptr, ptr2, size;struct mem *mem, *mem2;/*******第一:检测用户申请的内存块释放满足 LWIP 的规则*******//*******第二:从内存堆中划分用户的内存块******//* 寻找足够大的空闲块,从最低的空闲块开始.*/for (ptr = mem_to_ptr(lfree); ptr < MEM_SIZE_ALIGNED - size;ptr = ((struct mem *)(void*)&ram[ptr])->next){mem = ptr_to_mem(ptr); /* 取它的地址 */ /* 空间大小必须排除内存块头大小 */if ((!mem->used) &&(mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {/* 这个地方需要判断 剩余的内存块是否可以申请 size 内存块 */if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size +SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {/* 上面注释一大堆,主要就是说,剩余内存可能连一个内存块的头都放不下了,这个时候就没法新建空内存块。其索引也就不能移动 *//* 指向申请后的位置,即:建立下一个未使用的内存块的头部。即:插入一个新空内存块 */ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + size);/*从 Ptr2 地址开始创建 mem2 的结构体 */mem2 = ptr_to_mem (ptr2);/* 调用(struct mem *)(void *)&ram[ptr]; */mem2->used = 0;/* 这个根据下面的 if(mem2->next != MEM_SIZE_ALIGNED)判定 */mem2->next = mem->next;mem2->prev = ptr; /* 空闲内存块的前一个指向上面分配的内存块 *//* 前一个内存块指向上面建立的空闲内存块 */mem->next = ptr2;mem->used = 1;/* 将当前分配的内存块标记为 已使用 *//* 如果 mem2 内存块的下一个内存块不是链表中最后一个内存块 (结束地址),那就将它下一个的内存块的 prve 指向 mem2 */if (mem2->next != MEM_SIZE_ALIGNED) {((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;}}else {/* 内存块太小了会产生的碎片 */mem->used = 1;}/* 这里处理:当分配出去的内存正好是 lfree 时,因为该内存块已经被分配出去了, 必须修改 lfree 的指向下一个最其前面的已释放的内存块*/if (mem == lfree) {struct mem *cur = lfree;/* 只要内存块已使用且没到结尾,则继续往后找 */while (cur->used && cur != ram_end) {cur = ptr_to_mem(cur->next);/* 下一个内存块 */}/* 指向找到的 第一个已释放的内存块。如果上面没有找到,则 lfree = lfree 不变 */lfree = cur;}/* 这里返回 内存块的空间的地址,排除内存块的头 */return (u8_t *)mem + SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET; }}return NULL;}
}

4.LWIP内存池简介

(1)什么是内存池

lwip内存池是把连续的内存池分成多个大小相同的内存空间,并通过单链表的方式连接起来。当用户申请内存时,系统会从单链表的头 部取出一个内存块进行分配,释放内存时只需将内存块放回链表的头部。

lwip内存池优点:

分配速度快,防止内存碎片,回收便捷

lwip内存池缺点:

资源浪费,申请大型内存时,可能申请失败

lwip内存池应用场景:

在 lwIP 中,存在多种固定大小的数据结构,这些数据结构的特点是预先知道其大小并且在整个生命周期中保持不变。比如说,在建立 TCP 连接时,需要使用 TCP 控制块这种数据结构,其大小是固定的。为了满足这些数据结构的内存分配需求,lwIP 在内存初始化时创建了动态内存池 POOL预先分配一定数量的内存块。这种内存管理方式有助于提高内存分配的效率和性能。

(2)实现LWIP内存池的文件

对于内存堆中的动态内存池,运用了很多复杂的宏定义的运用。所以我们将重点探究这四个关键文件:memp.c、memp.h、memp_std.h 和 memp_prive.h。

i.memp_priv.h 文件

定义了memp(链接内存块)memp_desc(管理链接的内存块)结构体

memp 结构体将同一类型的内存池链表的形式连接起来

memp_desc 结构体则用于管理描述各种类型的内存池, 包括数量、大小、内存池的起始地址以及指向空闲内存池的指针。

/* 管理内存块 */
struct memp {struct memp *next;
};
​
/* 管理和描述各类型的内存池 */
struct memp_desc {/** 每个内存块的大小 */u16_t size;/** 内存块的数量 */u16_t num;/** 指向内存的基地址 */u8_t *base;/** 每个池的第一个空闲元素。元素形成一个链表 */struct memp **tab;
};

memp结构体和memp_desc结构体的关系:

由图可知:每一个 memp_desc 结构体都是用于管理同一类型的内存池。 这些内存池,也就是内存块,是通过链表的形式相互连接起来的。

ii.memp_std.h 文件

该文件是 lwIP 内存池的核心定义,申请了所需的内存池。使用了宏定义来确定是否启用特定类型的内存池,例如 TCP、UDP、DHCP、ICMP 等协议

#if LWIP_RAW
LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb),"RAW_PCB")
#endif /* LWIP_RAW */
​
#if LWIP_UDP
LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
#endif /* LWIP_UDP */
​
#if LWIP_TCP
LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, 
sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
#endif /* LWIP_TCP */

通过上面代码,可发现:不同类型的内存池是通过相应的宏定义声明启用的。LWIP_MEMPOOL 这个宏定义用于初始化各种类型的内存池。

iii. memp.h 文件

文件主要定义了 memp_t 枚举类型,该类型用于获取各类内存池的数量声明了宏定义和函数以提供外部文件使用,这里定义里前面所提到了LWIP_MEMPOOL 宏定义,

typedef enum {
​
/* ##为 C 语言的连接符,例如 MEMP_##A,A = NAME ,所以等于 MEMP_NAME */
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
​
#include "lwip/priv/memp_std.h"MEMP_MAX} memp_t;
​
#include "lwip/priv/memp_priv.h" /* 该文件需要使用上面的枚举 */
#include "lwip/stats.h"

根据 memp_std.h 文件中启用的内存池类型来计算 MEMP_MAX,即各类内存池的最大数量。计算方法如下:

1,LWIP_MEMPOOL 宏定义指向 MEMP_##name(##为 C 语言中的连接符)

2,通过#include "lwip/priv/memp_std.h"文件来启用所需的内存池类型

iv.memp.c 文件

在memp.h当中,我们有提到:LWIP_MEMPOOL 指向 LWIP_MEMPOOL_DECLARE 宏定义,而在memp.c 文件当中的const memp_pools[MEMP _MAX]数组则用于管理各类内存池的描述符

#define LWIP_MEMPOOL(name,num,size,desc) \LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"

当 memp_std.h 文件只启用 LWIP_RAW 和 LWIP_UDP 类型的内存池时,展开后的枚举类型如下:

u8_t memp_memory_RAW_PCB_base[((((((num) * (MEMP_SIZE +
(((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))))) +
MEM_ALIGNMENT - 1U)))];
​
static struct memp *memp_tab_RAW_PCB;
const struct memp_desc memp_RAW_PCB= { \LWIP_MEM_ALIGN_SIZE(size), \(num), \memp_memory_TCPIP_MSG_API_base, \&memp_tab_TCPIP_MSG_API \};u8_t memp_memory_UDP_PCB_base[((((((num) * (MEMP_SIZE +
(((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))))) +
MEM_ALIGNMENT - 1U)))];
​
static struct memp *memp_tab_UDP_PCB;const struct memp_desc memp_UDP_PCB= { \LWIP_MEM_ALIGN_SIZE(size), \(num), \memp_memory_UDP_PCB_base, \&memp_tab_UDP_PCB \};

这段代码通过使用 LWIP_MEMPOOL_DECLARE 宏定义,声明了各类内存池的描述和管理信息。具体来说,它定义了memp_desc 结构体,比如 :

memp_RAW_PCB:用于描述该类型的内存池的数量、大小、分配内存地址以及指向空闲内存池的指针。

const struct memp_desc* const memp_pools[MEMP_MAX] = {&memp_memp_RAW_PCB,&memp_memp_UDP_PCB,
};

memp_pools[MEMP_RAW_PCB],它取自 memp_RAW_PCB 变 量的地址。这是我们在前面展开LWIP_MEMPOOL_DECLARE 宏定义时定义的变量。

v.memp_init 函数和 memp_init_pool 函数

该函数是内存池的初始化

void memp_init(void)
{u16_t i;/* 遍历,需要多少个内存池 */for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {memp_init_pool(memp_pools[i]);}
}
​
void memp_init_pool(const struct memp_desc *desc)
{int i;struct memp *memp;*desc->tab = NULL;/* 内存对齐 */memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);/* 将内存块链接成链表形式 */for (i = 0; i < desc->num; ++i) {memp->next = *desc->tab;*desc->tab = memp;/* 地址偏移*/ memp = (struct memp *)(void *)((u8_t *)memp +
MEMP_SIZE + desc->size);}
}
​

每个类型的描述符都是用于管理和描述该类型的内存池。这些同一类型的内存池内部通过指向下一个节点的指针链接起来,形成一个链表。通过第二个 for 循 环语句,我们可以遍历这些同一类型的内存池,并将其以链表的形式进行链接。

memp_pool 数组包含了不同类型的内存池描述符,每个描述符负责管理同类型的内存池。这些内存池通过指针链接成一个单向链表,方便管理和访问。同一类型的内存池都 在同一个数组中分配,通过 base 指针可以找到该数组的首地址。tab 指针指向第一个空闲的内 存池,当用户申请内存池时,将从 tab 指针指向的内存池进行分配。分配完成后,tab 指针将偏移到下一个空闲内存池的地址,以便下次分配。

vi.memp_malloc 函数和 memp_malloc_pool 函数

内存池有多种类型,因此用户在申请内存池时需要**明确申请的类型lwIP 内存池的申请函数是 memp_malloc**

void *
memp_malloc(memp_t type)
{void *memp;memp = do_memp_malloc_pool(memp_pools[type]);return memp;
}
​
static void*
do_memp_malloc_pool(const struct memp_desc *desc)
{struct memp *memp;memp = *desc->tab;if (memp != NULL){*desc->tab = memp->next;return ((u8_t*)memp + MEMP_SIZE);}else{}return NULL;
}
​

memp_malloc 函数根据用户传入的内存池类型,如 UDP_PCB 等,在 memp_pool 数组中查找对应的内存池描述符。一旦找到对应的描述符,该函数会根据描述符中的 tab 指针来分配内存给用户,并将 tab 指针偏移至下一个空闲内存池。

vii.memp_free 函数与 memp_free_pool 函数

内存池的释放函数相对简单,它需要传入两个参数:内存池的类型和要释放的内存池的地址。通过这两个参数,lwIP 内核可以确定该类型内存池描述符的位置,以及需要释放的内存池的具体位置。

void memp_free(memp_t type, void *mem)
{if (mem == NULL) /* 判断内存块的起始地址释放为空 */{return;}do_memp_free_pool(memp_pools[type], mem);
}
​
static void do_memp_free_pool(const struct memp_desc* desc, void *mem)
{struct memp *memp;/* 据内存块的地址偏移得到内存块的起始地址 */memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);/* 内存块的下一个就是链表中的第一个空闲内存块 */memp->next = *desc->tab;/* *desc->tab 指向 memp 内存块中 */*desc->tab = memp;
}

释放函数很简单,只需要将内存池描述符的 tab 指针偏移至要释放的内存池。这样,释放的内存块就会被返回到相应的内存池中,以供后续使用。

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

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

相关文章

使用微服务Spring Cloud集成Kafka实现异步通信(消费者)

1、本文架构 本文目标是使用微服务Spring Cloud集成Kafka实现异步通信。其中Kafka Server部署在Ubuntu虚拟机上&#xff0c;微服务部署在Windows 11系统上&#xff0c;Kafka Producer微服务和Kafka Consumer微服务分别注册到Eureka注册中心。Kafka Producer和Kafka Consumer之…

Mybatis框架梳理

Mybatis框架梳理 前言1.ORM2.模块划分2.1 ORM的实现2.2 SQL的映射2.3 插件机制2.4 缓存机制2.5 其他 3. 愿景 前言 如果让我聊一聊mybatis&#xff0c;我该怎么说呢&#xff1f;开发中时时刻刻都在用它&#xff0c;此时此刻&#xff0c;脑海中却只浮现ORM框架这几个字&#xff…

[每周一更]-(第117期):硬盘分区表类型:MBR和GPT区别

文章目录 1. **支持的磁盘容量**2. **分区数量**3. **引导方式**4. **冗余和数据恢复**5. **兼容性**6. **安全性**7. **操作系统支持**8. 对比 国庆假期前补一篇 在一次扫描机械硬盘故障的问题&#xff0c;发现我本机SSD和机械硬盘的分类型不一样&#xff0c;分别是GPT和MBR&a…

Vue3轻松实现前端打印功能

文章目录 1.前言2.安装配置2.1 下载安装2.2 main.js 全局配置3.综合案例3.1 设置打印区域3.2 绑定打印事件3.3 完整代码4.避坑4.1 打印表格无边框4.2 单选框复选框打印不选中4.3 去除页脚页眉4.4 打印內容不自动换行1.前言 vue3 前端打印功能主要通过插件来实现。 市面上常用的…

C语言 | Leetcode C语言题解之第450题删除二叉搜索树中的节点

题目&#xff1a; 题解&#xff1a; struct TreeNode* deleteNode(struct TreeNode* root, int key){struct TreeNode *cur root, *curParent NULL;while (cur && cur->val ! key) {curParent cur;if (cur->val > key) {cur cur->left;} else {cur c…

telnet发送邮件教程:安全配置与操作指南?

telnet发送邮件的详细步骤&#xff1f;怎么用telnet命令发邮件&#xff1f; 尽管现代邮件客户端和服务器提供了丰富的功能和安全性保障&#xff0c;但在某些特定场景下&#xff0c;了解如何使用telnet发送邮件仍然是一项有价值的技能。AokSend将详细介绍如何安全配置和操作tel…

github/git密钥配置与使用

零、前言 因为要在ubuntu上做点东西&#xff0c;发现git clone 的时候必须输账户密码&#xff0c;后来发现密码是token&#xff0c;但是token一大串太烦了&#xff0c;忙了一天发现可以通过配置 公钥 来 替代 http 的 部署方式。 一、生成 ssh 密钥对 我们先测试下能不能 连接…

C语言复习概要(一)

本文 C语言入门详解&#xff1a;从基础概念到分支与循环1. C语言常见概念1.1 程序的基本结构1.2 变量作用域和存储类1.3 输入输出1.4 编译与运行 2. C语言中的数据类型和变量2.1 基本数据类型2.2 变量的声明与初始化2.3 常量与枚举 3. C语言的分支结构3.1 if语句3.2 if-else语句…

0108 Spring Boot启动过程

Spring Boot 的启动过程可以分为以下几个关键步骤&#xff1a; 1. SpringApplication 初始化 Spring Boot 应用的启动是通过调用 SpringApplication.run() 方法完成的。在这个过程中&#xff0c;Spring Boot 会通过 SpringApplication 类对应用进行初始化&#xff0c;包括设置…

国庆节快乐前端(HTML+CSS+JavaScript+BootStrap.min.css)

一、效果展示 二、制作缘由 最近&#xff0c;到了国庆节&#xff0c;自己呆在学校当守校人&#xff0c;太无聊了&#xff0c;顺便做一个小demo帮祖国目前庆生&#xff01;&#xff01;&#xff01; 三、项目目录结构 四、准备工作 (1)新建好对应的文件目录 为了方便&#xff…

Linux驱动开发(速记版)--设备树插件

第六十八章 设备树插件介绍 Linux 4.4之后引入了动态设备树&#xff0c;其中的设备树插件&#xff08;Device Tree Overlay&#xff09;是一种扩展机制&#xff0c;允许在运行时动态添加、修改或删除设备节点和属性。 设备树插件机制通过DTS&#xff08;设备树源文件&#xff0…

挖矿病毒记录 WinRing0x64.sys

之前下载过福晰pdf编辑器&#xff0c;使用正常。 某天发现机器启动后&#xff0c;过个几分钟(具体为5min)会自动运行几个 cmd 脚本(一闪而过)&#xff0c;但是打开任务管理器没有发现异常程序&#xff08;后面发现病毒程序伪装成System系统程序&#xff0c;见下图&#xff09;…

SpringCloud Config配置中心 SpringCloud Bus消息总线

一、SpringCloud Config 使用git储存配置信息 1&#xff09;什么是 SpringCloud Config项目实现的目标是将配置文件从本地项目中抽出来放到git仓库中&#xff0c;项目启动时自动从git仓库中取配置文件。 但是本地项目不直接和git仓库通信&#xff0c;而是通过配置服务器中转。…

python如何查询函数

1、通用的帮助函数help() 使用help()函数来查看函数的帮助信息。 如&#xff1a; import requests help(requests) 会有类似如下输出&#xff1a; 2、查询函数信息 ★查看模块下的所有函数&#xff1a; dir(module_name) #module_name是要查询的函数名 如&#xff1a; i…

vmvare虚拟机centos 忘记超级管理员密码怎么办?

vmvare虚拟机centos 忘记超级管理员密码怎么办?如何重置密码呢? 一、前置操作 重启vmvare虚拟机的过程中,长按住Shift键 选择第一个的时候,按下按键 e 进入编辑状态。 然后就会进入到类似这个界面中。 在下方界面 添加 init=/bin/sh,然后按下Ctrl+x进行保存退出。 init=/bi…

iPhone、iPad、iOS储存空间不足,瘦身终极方法

如果你实在是需要瘦身&#xff0c;但是确实没有什么可以删除了&#xff0c;也不想备份到其他地方&#xff0c;就这样做。 删除不需要的自带应用。 你可以删除FaceTime、股票、等app&#xff0c;但是不要删除你需要的app。 删除的界限是这样的&#xff1a;你永远都不可能使用…

OceanBase企业级分布式关系数据库

简介 OceanBase 数据库是阿里巴巴和蚂蚁集团不基于任何开源产品&#xff0c;完全自研的原生分布式关系数据库软件&#xff0c;在普通硬件上实现金融级高可用&#xff0c;首创“三地五中心”城市级故障自动无损容灾新标准&#xff0c;具备卓越的水平扩展能力&#xff0c;全球首…

Git版本控制工具--关于命令

Git版本控制工具 学习前言 在项目开发中&#xff0c;总是需要多个人同时对一个项目进行修改&#xff0c;如何高效快速地进行修改&#xff0c;且控制各自修改的版本不会和他人的进行重叠&#xff0c;这就需要用到Git分布式版本控制器了 作用 解决了一致性&#xff0c;并发性…

CSS 圆形边框与阴影

目录 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影 1. 圆角边框 1.1 正圆 1.2 圆角矩形 1.3 任意圆角 1.4 某个圆角 2. 盒子阴影 3. 文字阴影

Megabit兆比特10月比特币激增做好准备-最新加密货币新闻

Kaiko Research最近的分析表明&#xff0c;交易员正在积极为潜在的强劲表现做好准备特币(BTC)比今年十月。目前&#xff0c;BTC的交易价格为60800美元&#xff0c;在测试了60000美元的支撑位后&#xff0c;最近上涨了800美元。Megabit兆比特自成立以来,Megabit凭借用户友好的界…