STM32F4_内存管理(Malloc、Free)

news2024/9/19 10:47:20

目录

前言

1. 内存管理介绍

1.1 分块式内存管理

2. 实验程序

2.1 main.c

2.2 Malloc.c

2.3 Malloc.h


前言

        相信大家在学习C语言的过程中,都会学习到 malloc 动态开辟函数free 释放内存函数;这两个函数带给我们的优越性是:

        我们在使用一块内存的时候,通常都不会恰到好处的使用完定义的内存,可能我们定义的内存不够我们使用,也可能我们定义的内存会多,这样会造成内存浪费;所以在此基础之上引入malloc动态开辟函数,可以在定义一块内存的基础之上,随着我们的需要进行动态内存开辟;简单来说就是,定义的内存不够使用,就随着我们的需求一次一次的动态开辟内存;如果动态开辟的内存多余,就是要 free 函数释放掉动态开辟的空间;当然,当程序运行完以后,动态开辟的内存会自动的释放掉!

        STM32扩展的内存也存在这种情况;如果我们每次使用扩展内存的时候,都定义一个峰值的存储空间,那么难免造成内存空间的浪费,所以本节我们学习基于STM32的 malloc 动态内存开辟!

如果对malloc动态开辟函数和 free 空间释放函数不够了解,可参照以下笔记进行学习!!!

C语言_动态内存管理_light_2025的博客-CSDN博客

1. 内存管理介绍

        内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现有很多种,其中最终都是要实现两个函数malloc动态内存申请函数、free动态内存释放函数

1.1 分块式内存管理

        分块式内存管理主要由内存池内存管理表两部分组成。内存池被等分为n块,对应的内存管理表大小也为n,内存管理表的每一项对应内存池的一块内存。

内存管理表的项数表示的意义是:

        如果项数为0,那么相对应的内存块数量也为0,也就表示此时没有内存块被占用;如果项数非零,那么对应的内存块数也非零,此时项数为几对应的内存块数也为几,代表对应的内存块已经被占用,其数值代表被连续占用的内存块数。比方说:如果项数为10,那么算上这个项数,就表示分配了10个内存块给外部的指针。

内存分配方向:

        内存分配是从顶到底的分配方向。也就是首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。

分配原理:

        当指针p调用malloc函数动态申请内存的时候,先去判断指针p要分配的内存块数m,然后从末端开始查找,直到找到m块连续的空内存块,然后把这m块空的内存块的内存管理项设置为m,表示对应块的内存被占用,最后将这m块对应的内存块地址返回给指针p,完成一次分配。

        :如果从末端开始查找,查找结束还是没有找到对应的连续空内存块,就表示内存不足,此时将NULL返回给指针p,表示分配失败。

释放原理:

        当指针p申请的动态内存用完,需要释放的时候,调用free函数实现。free函数先找到内存地址所对应的内存块,然后找到与内存块所对应的内存管理表项目,得到指针p所占用的内存块数目m。然后将这m个内存管理表的项目都清零,标记完成一次内存释放。

2. 实验程序

实验功能:

        开机后,显示提示信息、KEY0用于申请内存,每次申请2K字节内存。KEY1用于写数据到申请的内存里面。KEY2用于释放内存。KEY_UP用于切换操作内存区(内部SRAM内存/外部SRAM内存/内部CCM内存)

2.1 main.c

#include "stm32f4xx.h"                 
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "Key.h"
#include "usmart.h"
#include "SRAM.h"
#include "Malloc.h"

 
//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
	LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
	led_set(sta);
}
 
int main(void)
{        
	u8 key;		 
 	u8 i=0;	    
	u8 *p=0;
	u8 *tp=0;
	u8 paddr[18];				//存放P Addr:+p地址的ASCII值
	u8 sramx=0;					//默认为内部SRAM

	delay_init(168);  //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	
	LED_Init();					//初始化LED 
 	LCD_Init();					//LCD初始化  
 	Key_Init();					//按键初始化 
 	FSMC_SRAM_Init();			//初始化外部SRAM  
	
	my_mem_init(SRAMIN);		//初始化内部内存池
	my_mem_init(SRAMEX);		//初始化外部内存池
	my_mem_init(SRAMCCM);		//初始化CCM内存池
	
 	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");	
	LCD_ShowString(30,70,200,16,16,"MALLOC TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2023/08/02");   
	LCD_ShowString(30,130,200,16,16,"KEY0:Malloc  KEY2:Free");
	LCD_ShowString(30,150,200,16,16,"KEY_UP:SRAMx KEY1:Read"); 
 	POINT_COLOR=BLUE;//设置字体为蓝色 
	LCD_ShowString(30,170,200,16,16,"SRAMIN");
	LCD_ShowString(30,190,200,16,16,"SRAMIN  USED:   %");
	LCD_ShowString(30,210,200,16,16,"SRAMEX  USED:   %");
	LCD_ShowString(30,230,200,16,16,"SRAMCCM USED:   %");
 	while(1)
	{	
		key=KEY_Scan(0);//不支持连按	
		switch(key)
		{
			case 0://没有按键按下	
				break;
			case KEY0_PRESS:	//KEY0按下
				p=mymalloc(sramx,2048);//申请2K字节
				if(p!=NULL)
					sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容
				break;
			case KEY1_PRESS:	//KEY1按下	   
				if(p!=NULL)
				{
					sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容 	 
					LCD_ShowString(30,270,200,16,16,p);			 //显示P的内容
				}
				break;
			case KEY2_PRESS:	//KEY2按下	  
				myfree(sramx,p);//释放内存
				p=0;			//指向空地址
				break;
			case KEY_UP_PRESS:	//KEY UP按下 
				sramx++; 
				if(sramx>2) //因为只有三个存储区
					sramx=0;
				if(sramx==0)
					LCD_ShowString(30,170,200,16,16,"SRAMIN "); //内部SRAM存储区
				else if(sramx==1)
					LCD_ShowString(30,170,200,16,16,"SRAMEX "); //外部扩展存储区
				else 
					LCD_ShowString(30,170,200,16,16,"SRAMCCM"); //内部CCM存储区
				break;
		}
		if(tp!=p)
		{
			tp=p;
			sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);
			LCD_ShowString(30,250,200,16,16,paddr);	//显示p的地址
			if(p)
				LCD_ShowString(30,270,200,16,16,p);//显示P的内容
		    else 
				LCD_Fill(30,270,239,266,WHITE);	//p=0,清除显示
		}
		delay_ms(10);   
		i++;
		if((i%20)==0)//DS0闪烁.
		{
			LCD_ShowNum(30+104,190,my_mem_perused(SRAMIN),3,16);//显示内部内存使用率
			LCD_ShowNum(30+104,210,my_mem_perused(SRAMEX),3,16);//显示外部内存使用率
			LCD_ShowNum(30+104,230,my_mem_perused(SRAMCCM),3,16);//显示CCM内存使用率
 			LED0=!LED0;
 		}
	}	   
}

2.2 Malloc.c

#include "stm32f4xx.h"            
#include "Malloc.h"


//内存池(32字节对齐)
__align(32) u8 mem1base[MEM1_MAX_SIZE];  //内部SRAM内存池
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0x68000000)));  //外部SRAM内存池
__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0x10000000)));  //内部CCM内存池
//注:
//	__align(32)是负责内存对齐的宏,它会补充一些数据以便下面的数据对齐;简单来说就是让接下来的数据或者指令32位对齐
//  __attribute__((at(0x68000000)))表示定义的数据的起始地址是从0x68000000开始的
//	u8 mem1base[MEM1_MAX_SIZE];表示拿出内部内存池的32K的空间来做实验。之所以定义u8类型,是因为计算机内存都是以字节为单位的存储空间
//	内存中的每个字节都有唯一的编号,这个编号叫做地址。
//	不管存储什么数据类型的地址编号都是32位的,1个字节是8位,那么每个地址编号可以容纳四个字节,这也是为什么定义变量之前一定要先声明数据类型的原因,一个字符char占1个字节,一个整型int占4个字节
//	__align(32)让接下来的数据和指令32位对齐,也就是让对应的地址编号和指针32位对齐

//内存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];  //内部SRAM内存管理表  实际上就是u16 mem1mapbase[3200],意思是定义一个有3200个内存块的数组
//	数组名mem1mapbase就是mem1mapbase[0] 该数组的第一个元素代表3200个内存块中的第一个内存块
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));  //外部SRAM内存池
u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE)));  //内部CCM内存池
//注:之所以是__attribute__((at(0X68000000+MEM2_MAX_SIZE)));是因为分块式内存管理下,外部SRAM区动态开辟时,是从末端依次向里的,所以起始地址应该是0x68000000加上最大的外部管理内存MEM2_MAX_SIZE

//内存管理参数
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //内存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE}; //内存分块大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE};   //内存总大小


//内存管理控制器   初始化结构体成员
struct _m_malloc_dev malloc_dev=
{
	my_mem_init,  //内存初始化
	my_mem_perused, //内存使用率
	mem1base,mem2base,mem3base,  //内存池  mem1base有100K元素,mem2base有960K元素,mem3base有60K元素
	mem1mapbase,mem2mapbase,mem3mapbase,  //内存管理状态表
	0,0,0,        //内存管理未就绪
};

//复制内存
//*destinct:目的地址
//*source:源地址
//n:需要复制的内存长度(单位字节)
void mymemcpy(void *destinct,void *source,u32 n) //该函数表示从source处复制n个字节长度的数据到destinct
{
	u8 *xdestinct=destinct; //定义一个指针xdestinct指向目标地址的首元素
	u8 *xsource=source;   //定义一个指针xsource指向源地址的首元素
	while(n--) //通过while循环一个字节一个字节的复制
	{
		*xdestinct++=*xsource++; //字符串拷贝函数的逻辑,
	}
}
//设置内存
//*S:内存首地址
//C:要设置的值
//Count:需要设置的内存大小(字节为单位)
void mymemset(void *S,u8 C,u8 Count)
{
	u8 *xs=S;  //定义一个新指针*xs指向内存首地址对应的元素
	while(Count--) //每设置一个,需要设置的内存大小就减少一点
		*xs++=C;  //把u8类型数据C填充到以指针变量xs为首地址的内存空间中,填充个数由Count值决定
}
//内存管理初始化
//memoryx:所属的内存块
void my_mem_init(u8 memoryx)
{
	mymemset(malloc_dev.memmap[memoryx],0,memtblsize[memoryx]*2); //内存状态表数据清零
	//第一个参数是内存首地址,malloc_dev.memmap[memoryx]调用结构体表示拿到了内存池中第一个内存块的地址,以此地址作为存储内存的首地址
	//初始化设置状态表数据为0
	//清零自然是整个内存表的数据全部清零,所以设置的大小就是内存块对应的整个内存表的大小
	//之所以乘以2是因为:调用的mymemset函数是将u8类型的数据C填充到对应内存块的首地址,而设置的*memmap[SRAMBANK]是u16位的
	//所以需要乘以2保证将其全部清零
	mymemset(malloc_dev.membase[memoryx],0,memsize[memoryx]);  //内存池数据清零
	//对应内存池设置的结构体u8 *membase[SRAMBANK];所以不需要乘以2就可以实现内存池数据清零
	malloc_dev.memrdy[memoryx]=1;  //内存管理初始化OK,0表示内存管理初始化未就绪
}
//获取内存使用率
//memoryx:所属的内存块  也可以说是我们想要获得哪个内存块的使用率
//返回值:使用率(0~100)
u8 my_mem_perused(u8 memoryx)
{
	u32 used=0;
	u32 i;
	for(i=0;i<memtblsize[memoryx];i++) //循环范围是在整个内存块个数之间循环
	{
		if(malloc_dev.memmap[memoryx][i]) //内存管理表和内存块是一一对应的,内存块分配内存时,如果对应内存块的内存没有被占用,那么标记为0
			//只要申请开辟内存,那么首先就要获取需要开辟的内存块数m,然后从末端依次开始开辟m个连续的内存块,只要内存足够,就将这m个连续的内存块标记为m,表示已经被占用了
		//所以只要内存被占用,那么对应的内存块的数据就不为0,至于对应数据是几,那要根据所需的内存块数进行判断
		//换句话说就是,如果对应内存块被占用,那么if判断语句一定为真
		{
			used++; //定义一个变量表示使用率,单位%,只要对应内存块被占用,used++
		}
	}
	return (used*100)/(memtblsize[memoryx]); //该公式很简单,被占用的内存块数除以有多少个内存块,然后乘以100%转换成百分比
}
//内存分配(内部调用)
//memory:所属内存块
//size:要分配的内存大小(字节)
//返回值:0xFFFFFFFF,代表错误;其他,内存偏移地址
u32 my_mem_malloc(u8 memory,u32 size)
{
	signed long offset=0;
	u32 need_memory; 		//需要的内存块数
	u32 continue_memory=0;  //连续的空内存块数
	u32 i;
	if(!malloc_dev.memrdy[memory]) //!malloc_dev.memrdy[memory]为真,则表示malloc_dev.memrdy[memory]为假
		//malloc_dev.memrdy[memory]=0表示内存管理还未就绪
	{
		malloc_dev.init(memory); //内存管理还没有就绪,那么先执行初始化
	}
	if(size==0) //表示要分配的内存大小为0
	{
		return 0xFFFFFFFF; 
	}
	need_memory=size/memblksize[memory];  //得到需要分配的连续内存块数
	//size是总的要分配的内存大小;memblksize[memory]表示所属内存块的大小,相除表示需要多少个内存块才可以储存size大小的内存
	if(size%memblksize[memory]) //该if语句表示分配过后,所需内存块的个数不是整数,结合实际,不能使用半个或者几分之几个内存块
		//所以在原本需要的内存块数上++
	{
		need_memory++;
	}
	for(offset=memtblsize[memory]-1;offset>=0;offset--)  //搜索整个内存控制区,寻找有无连续个need_memory个内存块
	//offset-- 表示从内存管理表的末端开始搜索,依次向前搜索need_memory个连续的内存块
	{
		if(!malloc_dev.memmap[memory][offset]) //整体!malloc_dev.memmap[memory][offset]取反为真,那么malloc_dev.memmap[memory][offset]为假
			//malloc_dev.memmap[memory][offset]=0;表示对应的内存块的数值为0,对应内存块为空,也就表示该内存块可以被使用
		{
			continue_memory++; //连续空内存块数增加
		}
		else
			continue_memory=0; //只要找到一个内存块不为空,那么就表示所需的内存块不连续了,continue_memory=0;
		if(continue_memory==need_memory) //搜索找到的连续的内存块数恰好等于所需的内存块数
		{
			for(i=0;i<need_memory;i++) //标记这些连续的所需的内存块非空
			{
				malloc_dev.memmap[memory][offset+i]=need_memory; //将这些所需的连续的内存块的数据改为need_memory
			}
			return (offset*memblksize[memory]);  //返回偏移地址
		}
	}
	return 0xFFFFFFFF; //未找到符合分配条件的内存块
}
//释放内存
//memory:所属的内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败
u8 my_mem_free(u8 memory,u32 offset)
{
	int i;
	if(!malloc_dev.memrdy[memory])//整体为真,则表示malloc_dev.memrdy[memory]为假,表示内存管理未就绪
	{
		malloc_dev.init(memory); //调用结构体指针函数指向初始化函数
		return 1;//释放失败,未初始化
	}
	if(offset<memsize[memory]) //偏移在内存池内
	{
		int index=offset/memblksize[memory];	 //偏移所在内存块号,也就是得到从末端开始偏移,偏移到第几块内存块
		int nmemb=malloc_dev.memmap[memory][index]; //定义变量记录内存块的数量
		for(i=0;i<nmemb;i++)  //内存块清零
		{
			malloc_dev.memmap[memory][index+i]=0; 
		}
		return 0; //内存释放成功
	}
	else
		return 2; //偏移超区了
}
//释放内存(外部调用)
//memory:所属内存块
//ptr:内存首地址
void myfree(u8 memory,void *ptr)
{
	u32 offset;
	if(ptr==NULL)
		return;  //首元素地址指向空指针,地址为空
	offset=(u32)ptr-(u32)malloc_dev.membase[memory]; //记录偏移量
	my_mem_free(memory,offset); //释放内存
}
//分配内存(外部调用)
//memory:s所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址
void *mymalloc(u8 memory,u32 size)
{
	u32 offset;
	offset=my_mem_malloc(memory,size); //offset接收到动态开辟内存以后,返回的地址偏移量,得到偏移量就表示得到了动态开辟内存的大小
	if(offset==0xFFFFFFFF)
		return NULL;
	else
		return (void*)((u32)malloc_dev.membase[memory]+offset);	//该语句是C语言malloc动态开辟空间内存的表示
}
//重新分配内存(外部调用)
//memory:所属内存块
//*ptr:旧内存首地址
//size:要分配的内存大小(字节)
//返回值:新分配的内存首地址
void *myrealloc(u8 memory,void *ptr,u32 size)
{
	u32 offset;    
    offset=my_mem_malloc(memory,size);   	
    if(offset==0XFFFFFFFF)return NULL;     
    else  
    {  									   
	    mymemcpy((void*)((u32)malloc_dev.membase[memory]+offset),ptr,size);	//拷贝旧内存内容到新内存   
        myfree(memory,ptr);  											  		//释放旧内存
        return (void*)((u32)malloc_dev.membase[memory]+offset);  				//返回新内存首地址
    }  
}



2.3 Malloc.h

#ifndef _Malloc__H_
#define _Malloc__H_
#include "stm32f4xx.h" 

/
#ifndef NULL

#define NULL 0   //宏定义空指针返回0,C语言标准规定当内存不足时,或者动态开辟内存失败时,返回空指针

#endif
/

//定义三个内存池
#define SRAMIN 0  //STM32内部的SRAM内存池 192K
#define SRAMEX 1  //STM32外部的SRAM扩展内存池 1M
#define SRAMCCM 2  //CCM内存池(此部分SRAM仅仅CPU可以访问!!!)

#define SRAMBANK 3  //定义支持的SRAM块数  这里定义支持的块数是3,包括内部SRAM、外部SRAM、CCM内存池

//Memory1内存参数设定,Memory1完全处于内部SRAM里面 192K
#define MEM1_BLOCK_SIZE 32    //内存块大小为32个字节,也就是说我动态开辟了1个内存块,就相当于开辟了一个32字节大小的空间
#define MEM1_MAX_SIZE 100*1024  //最大管理内存 100K  1K=1024个字节  相当于我设定的内存峰值是100K
#define MEM1_ALLOC_TABLE_SIZE  MEM1_MAX_SIZE/MEM1_BLOCK_SIZE  //内存管理表大小 相当于设置了3200个内存块  也就是说将内部SRAM内存池划分为3200个内存块

//Memory2内存参数设定,Memory2的内存池处于外部SRAM里面 1M
#define MEM2_BLOCK_SIZE 32   //内存块大小32个字节
#define MEM2_MAX_SIZE 960*1024  //外部SRAM最大管理内存960K
#define MEM2_ALLOC_TABLE_SIZE  MEM2_MAX_SIZE/MEM2_BLOCK_SIZE  //内存管理表大小 相当于设置了30720个内存块

//Memory3内存参数设定,Memory3处于CCM,用于管理CCM(特别注意:这部分的SRAM,仅CPU可以访问!!!)
#define MEM3_BLOCK_SIZE 32   //内存块大小为32个字节
#define MEM3_MAX_SIZE 60*1024  //内部CCM最大管理内存60K
#define MEM3_ALLOC_TABLE_SIZE  MEM3_MAX_SIZE/MEM3_BLOCK_SIZE  //内存管理表大小 相当于设置了1920个内存块


//内存管理控制器
struct _m_malloc_dev
{
	void (*init)(u8);   //定义一个init指针,该指针指向初始化函数,初始化函数有一个unsigned char型参数
	//void (*init)(u8); 定义了一个指向函数的指针变量,该指针变量名是init;void表示该函数没有返回值,u8是函数的形参
	//数据类型+(*变量名)(形参);
	u8 (*perused)(u8);  //定义一个perused指针,该指针指向一个内存使用率的函数,该函数有一个参数
	u8 *membase[SRAMBANK];  //定义一个membase指针指向内存池,管理三个(SRAMBANK)区域的内存
	u16 *memmap[SRAMBANK];  //定义一个memmap指针指向内存管理状态表,该数组成员表示三个SRAM块
//	u16 *memmap[3]={mem1mapbase,mem2mapbase,mem3mapbase};其中mem2mapbase就表示外部扩展的SRAM的第一个内存块的地址
//	也就是说该数组中对应的三个成员分别代表内部SRAM、外部扩展的SRAM、CCM的第一个内存块的地址
	u8 memrdy[SRAMBANK];   //内存管理是否就绪
};
extern struct _m_malloc_dev malloc_dev; //定义一个结构体变量,该变量在malloc.c里面定义




void mymemcpy(void *destinct,void *source,u32 n);
void mymemset(void *S,u8 C,u8 Count);
void my_mem_init(u8 memoryx);
u8 my_mem_perused(u8 memoryx);
u32 my_mem_malloc(u8 memory,u32 size);
u8 my_mem_free(u8 memory,u32 offset);
void myfree(u8 memory,void *ptr);
void *mymalloc(u8 memory,u32 size);
void *myrealloc(u8 memory,void *ptr,u32 size);



#endif


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

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

相关文章

[深度学习] GPU处理能力(TFLOPS/TOPS)

计算能力换算 理论峰值 &#xff1d; GPU芯片数量GPU Boost主频核心数量*单个时钟周期内能处理的浮点计算次数 只不过在GPU里单精度和双精度的浮点计算能力需要分开计算&#xff0c;以最新的Tesla P100为例&#xff1a; 双精度理论峰值 &#xff1d; FP64 Cores &#xff0a;…

Scratch Blocks自定义组件之「下拉图标」

一、背景 由于自带的下拉图标是给水平布局的block使用&#xff0c;放在垂直布局下显得别扭&#xff0c;而且下拉选择后回修改image字段的图片&#xff0c;这让我很不爽&#xff0c;所以在原来的基础上稍作修改&#xff0c;效果如下&#xff1a; 二、使用说明 &#xff08;1&am…

转机来了,国内全新芯片技术取得突破,关键驱动引擎开始提速

芯片技术转机来了 我们都知道&#xff0c;芯片技术是现代信息技术的基石&#xff0c;它驱动着计算机、智能手机、物联网设备等各类电子设备的运行。 科技的不断进步&#xff0c;芯片技术也在不断演进。 从传统的集成电路到现代的微处理器和系统芯片&#xff0c;其计算能力和能…

Total Variation loss

Total Variation loss 适合任务 图像复原、去噪等 处理的问题 图像上的一点点噪声可能就会对复原的结果产生非常大的影响&#xff0c;很多复原算法都会放大噪声。因此需要在最优化问题的模型中添加一些正则项来保持图像的光滑性&#xff0c;图片中相邻像素值的差异可以通过…

Pytorch深度学习框架入门

1.pytorch加载数据 唤醒指定的python运行环境的命令&#xff1a; conda activate 环境的名称 from torch.utils.data import Dataset #Dataset数据处理的包 from PIL import Image import os#定义数据处理的类 class MyData(Dataset):#数据地址处理方法def __init__(self,ro…

从《信息技术服务数据中心业务连续性等级评价准则》看数据备份

​​​​​​​ 5月23日&#xff0c;国家标准化管理委员会与国家市场监督管理总局发布了《信息技术服务数据中心业务连续性等级评价准则》&#xff0c;旨在适应各行各业逐步深入的数字化转型&#xff0c;提升全社会对数据中心服务中断风险的重视。 信息技术服务数据中心业务连续…

KL15 是什么?ACC,crank,on等

KL含义 KL is the abbreviation for klemme which is the German term for connector / connection.KL是“ klemme”的缩写&#xff0c;这是德语中连接器或连接的术语。 KL30 &#xff0c;通常表示电瓶的正极。positive KL31&#xff0c;通常表示电瓶的负极。negative KL15, 通…

【NLP概念源和流】 04-过度到RNN(第 4/20 部分)

接上文 【NLP概念源和流】 03-基于计数的嵌入,GloVe(第 3/20 部分) 一、说明 词嵌入使许多NLP任务有了显著的改进。它对单词原理图的理解以及将不同长度的文本表示为固定向量的能力使其在许多复杂的NLP任务中非常受欢迎。大多数机器学习算法可以直接应用于分类和回归任务的…

go初识iris框架(三) - 路由功能处理方式

继了解get,post后 package mainimport "github.com/kataras/iris/v12"func main(){app : iris.New()//app.Handle(请求方式,url,请求方法)app.Handle("GET","/userinfo",func(ctx iris.Context){path : ctx.Path()app.Logger().Info(path) //获…

MTS性能监控你知道多少

前言 说到MySQL的MTS&#xff0c;相信很多同学都不陌生&#xff0c;从5.6开始基于schema的并行回放&#xff0c;到5.7的LOGICAL_CLOCK支持基于事务的并行回放&#xff0c;这些内容都有文章讲解&#xff0c;在本篇文章不再赘述。今天要讲的是&#xff0c;你知道如何查看并行回放…

最新AI系统ChatGPT网站源码/支持GPT4.0/GPT联网功能/支持ai绘画/mj以图生图/支持思维导图生成

使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到系统&#xff01; 同步mj图片重新生成指令 同步 Vary 指令 单张图片对比加强 Vary(Strong) | Vary(Subtle) 同步 Zoom 指令 单张图片无限缩放 Zoom out 2x | Zoom out 1.5x 新增GPT联网提问功能、签到功能 一、功能演示 …

基于springboot生鲜物流系统-计算机毕设 附源码13339

springboot生鲜物流系统 摘要 生鲜产品易于腐烂、难贮存、不易长时间运输&#xff0c;生产者所面临的市场风险很大&#xff0c;很多生鲜产品无法实现“货畅其流”和“物尽其值”&#xff0c;适宜的生鲜产品物流体系就显得尤为重要。本文将广东省生鲜产品物流体系的构建作为一个…

删除链表中等于给定值 val 的所有节点

203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 给出链表 1->2->3->3->4->5->3, 和 val 3, 你需要返回删除3之后的链表&#xff1a;1->2->4->5。 分析思路&#xff1a;这道题的思路&#xff0c;与之前删除链表中重复的结点相似。 因…

腾讯云从业者认证考试考点——云网络产品

文章目录 腾讯云网络产品功能网络产品概述负载均衡&#xff08;Cloud Load Balancer&#xff09;私有网络&#xff08;Virtual Private Cloud&#xff0c;VPC&#xff09;专线接入弹性网卡&#xff08;多网卡热插拔服务&#xff09;NAT网关&#xff08;NAT Gateway&#xff09;…

了解 spring MVC + 使用spring MVC - springboot

前言 本篇介绍什么是spring MVC &#xff0c;如何使用spring MVC&#xff0c;了解如何连接客户端与后端&#xff0c;如何从前端获取各种参数&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1. 什么…

RD算法(四)登堂入室 —— 成像完成

SAR成像专栏目录_lightninghenry的博客-CSDN博客https://lightning.blog.csdn.net/article/details/122393577?spm=1001.2014.3001.5502先放RD算法最终的成像结果: 经简单的地距投影后为(地距投影的内容在后面的几何校正章节中讲解): 温哥华这地形还真像是一张怪兽的巨嘴呀…

商城-学习整理-基础-商品服务API-三级分类(五)

目录 一、启动之前创建的环境1、启动虚拟机2、启动mysql3、启动redis4、启动nacos5、导入三级分类测试数据 二、开发商品服务三级分类列表1、后台模块1&#xff09;书写商品三级分类表后台业务逻辑 2、前端模块1&#xff09;启动renren-fast前后端项目2&#xff09;在系统管理-…

js逆向 - X-Zse-96分析(JsRPC实战)

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01; 目标网站&#xff1a;aHR0cHM6Ly93d3cuemhpaHUuY29tLw 目标接口&#xff1a;aHR0cHM6Ly93d3cuemhpaHUuY29tL2FwaS92NC9zZWFyY2hfdjM …

卸载 Postman!一款 IDEA 神级插件,更便捷、高效...

Restful Fast Request 是 IDEA 版 Postman&#xff0c;它是一个强大的 restful api 工具包插件&#xff0c;可以根据已有的方法帮助您快速生成 url 和 params。 Restful Fast Request API 调试工具 API 管理工具 API 搜索工具。 它有一个漂亮的界面来完成请求、检查服务器…

Django智能养殖场管理系统

目 录 摘要 1 绪论 1.1研究背景与意义 1.2研究内容 1.3论文结构与章节安排 2 智能养殖场管理系统分析 2.1 可行性分析 2.2 系统业务流程分析 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 智能养殖场管理系统总体设计 …