FREERTOS源码解析——heap4.c

2023-05-16

目录

  • 内存管理
    • heap4无法解决的内存碎片:
  • HEAP4简析
    • 分配内存在哪,大小多少
    • 如何管理
  • 重要源码解析

内存管理

freertos目前提供了以下5种内存管理,特点如下

  1. heap1:最简单的内存管理,管理的其实是一个静态全局变量,只允许分配,不允许释放,设计之初就是用于创建信号量、任务、队列一般不需要释放的数据,不过在FreeRTOS V9.0.0及其以后版本添加 support for static allocation基本就被替代了。
  2. heap2.:比1多了内存释放,分配也因为有内存释放原因多了一个最佳适配算法,不过在内存释放后没有空闲块合并的功能,只适合信号量队列任务等大小固定的内存分配,随机大小的内存分配释放会因为内存碎片问题而无法进行。后面heap4是heap2的优化版。
  3. heap3:简单封装标准C库mallco和free,提供线程安全。
  4. heap4:一般来说FREERTOS五种堆管理里这个是最常见使用的了,首次适配算法,heap2的优化版,多了相邻内存碎片合并功能,内存碎片的风险少很多,不过没有MMU的芯片没有逻辑地址和物理地址的映射概念,内存分配就是一个萝卜一个坑,想做内存紧凑比较费劲。
  5. heap5:比heap4多了不连续地址多段内存管理,分配释放代码完全一样。

heap4无法解决的内存碎片:

在这里插入图片描述

本文主要解析heap4。

HEAP4简析

heap4的使用只需要两个接口pvPortMalloc()和vPortFree()。简要的说下重点

分配内存在哪,大小多少

静态全局变量,位于静态区

static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

大小由FreeRTOSConfig.h 文件里的宏定义控制

#define configTOTAL_HEAP_SIZE					((size_t)(40*1024))    

如何管理

freertos基本只对空闲块做管理,heap4也一样,空闲块做链表管理,并且链表排序内存地址从小到大管理,上文也说到,是首次适应算法,这么做主要便于内存空闲块合并。
分配:从空闲块找第一个大小合适的空闲块,空闲块大小减去需要的内存扔满足最小空闲块要求,该空闲块把剩余内存拆分出来做新的一个空闲块插入继续按地址排列继续插入内存空闲链表中
释放:根据释放首地址,释放对应内存块,将内存块按地址插入到空闲块链表里,如果空闲块与相邻内存块地址连续,则合并为一个大空闲内存块

图网上找的的,作者未知
在这里插入图片描述

在这里插入图片描述

  • xStart:静态场两,空闲块链表首节点,指向第一个空闲块节点
  • pxEnd:结构体指针,地址是ucHeap末尾,空闲链表尾节点,nextpoint指向null,标志链表结束。

heap4 分配模型图,A->F
在这里插入图片描述
结合图文,算是比较大概的介绍了heap4的管理模型,其中还有些字节对齐及一些细节内容由结合源码分析

重要源码解析

typedef struct A_BLOCK_LINK   //空闲块节点,具体的分配内存返回的的指针实际上是该块的下一个地址,
							//也就是说此结构体只是一个节点信息
{
	struct A_BLOCK_LINK *pxNextFreeBlock;	/*<< 下一个空闲块. */
	size_t xBlockSize;						/*<< 该块的大小. 最高位代表该块是否被分配*/
} BlockLink_t;

static void prvHeapInit( void )  //堆初始化,不需要用户调用,第一次pvPortMalloc()会执行该函数
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
size_t uxAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;

/* 8字节对齐,因为AAPCS规则要求堆栈8字节对齐 */
	uxAddress = ( size_t ) ucHeap;

	if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )  //
	{
		uxAddress += ( portBYTE_ALIGNMENT - 1 );
		uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
		xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
	}

	pucAlignedHeap = ( uint8_t * ) uxAddress;

	/* xStart 存储空闲节点连表首节点*/
	xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
	xStart.xBlockSize = ( size_t ) 0;

	/* pxEnd 和xStart不同,pxEND是一个指针指向堆空闲末尾,标记空闲块链表的结束*/
	uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
	uxAddress -= xHeapStructSize;
	uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
	pxEnd = ( void * ) uxAddress;
	pxEnd->xBlockSize = 0;
	pxEnd->pxNextFreeBlock = NULL;

	/* 第一个空闲块指针,指向堆空间字节对齐后的第一个节点,
	并且此空闲块的下一个节点是pxend,至此空闲块成链*/
	pxFirstFreeBlock = ( void * ) pucAlignedHeap;
	pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
	pxFirstFreeBlock->pxNextFreeBlock = pxEnd;

	/* 一个统计堆空闲历史最小值,一个显示当前堆空闲字节*/
	xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
	xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;

	/* 代表内存快是否分配的bit */
	xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

初始化函数,主要做了三件事,对初始堆空间做字节对齐,空闲节点串联,xFreeBytesRemaining等信息初始化。

void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;

	vTaskSuspendAll();   //不允许被打断,线程安全
	{
		/* If this is the first call to malloc then the heap will require
		initialisation to setup the list of free blocks. */
		if( pxEnd == NULL )  //末尾节点为null代表未初始化,第一次分配内存需要先对堆空间初始化
		{
			prvHeapInit();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* 高一位代表分配标记位,所以管理的的内存有最大值,申请的不能大于最大值*/
		if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
		{
			if( xWantedSize > 0 )
			{
				xWantedSize += xHeapStructSize;//分配内存是以块的形式分配,还要加上块节点结构的大小

				/*控制字节对齐,保证分配内存字节对齐 */
				if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
				{
					/* Byte alignment required. */
					xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
					configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
			//剩余内存够才分配
			if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
			{
				/* 遍历链表找到第一个满足要求的节点 */
				pxPreviousBlock = &xStart;
				pxBlock = xStart.pxNextFreeBlock;
				while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
				{
					pxPreviousBlock = pxBlock;
					pxBlock = pxBlock->pxNextFreeBlock;
				}

				/* 找到节点 */
				if( pxBlock != pxEnd )
				{
					/* pxPreviousBlock->pxNextFreeBlock为空闲块地址,真正返回地址的是块节
					点信息之后的数组 */
					pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );

					/* 节点从空闲表去除 */
					pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

					/* 拆分空闲节点 */
					if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
					{
						/* 分裂算法 */
						pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
						configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );

						/* 计算新节点信息 */
						pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
						pxBlock->xBlockSize = xWantedSize;

						/* 插入空闲快链表 */
						prvInsertBlockIntoFreeList( pxNewBlockLink );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					xFreeBytesRemaining -= pxBlock->xBlockSize;

					if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
					{
						xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/*分配块高一位置为标记以分配*/
					pxBlock->xBlockSize |= xBlockAllocatedBit;
					pxBlock->pxNextFreeBlock = NULL;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		traceMALLOC( pvReturn, xWantedSize );
	}
	( void ) xTaskResumeAll();

	#if( configUSE_MALLOC_FAILED_HOOK == 1 )
	{
		if( pvReturn == NULL )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif

	configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
	return pvReturn;
}

分配:调堆初始化、首次适配算法、分裂节点。

void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;

	if( pv != NULL )
	{
		/* 找到释放内存的内存块节点信息*/
		puc -= xHeapStructSize;

		
		pxLink = ( void * ) puc;

	
		configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
		configASSERT( pxLink->pxNextFreeBlock == NULL );

		if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) //确保该块已经分配,才能进行释放操作
		{
			if( pxLink->pxNextFreeBlock == NULL )
			{
				/* 标志位处理*/
				pxLink->xBlockSize &= ~xBlockAllocatedBit;

				vTaskSuspendAll();
				{
					/* 插入空闲块链表 */
					xFreeBytesRemaining += pxLink->xBlockSize;
					traceFREE( pv, pxLink->xBlockSize );
					prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
				}
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}

释放:没什么可讲的,内存空闲块合并在prvInsertBlockIntoFreeList函数处理

static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;

	/* 根据地址遍历,寻找插入点. */
	for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
	{
		/* Nothing to do here, just iterate to the right position. */
	}

	/* 判找到插入点后判断是否和前一个空闲块连续,连续则合并 */
	puc = ( uint8_t * ) pxIterator;
	if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
	{
		pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
		pxBlockToInsert = pxIterator;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/* 判找到插入点后判断是否和后一个空闲块连续,连续则合并 ,pxEnd标记堆空间结束是例外,不允许合并*/
	puc = ( uint8_t * ) pxBlockToInsert;
	if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
	{
		if( pxIterator->pxNextFreeBlock != pxEnd )
		{
			/* Form one big block from the two blocks. */
			pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
			pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
		}
		else
		{
			pxBlockToInsert->pxNextFreeBlock = pxEnd;
		}
	}
	else
	{
		pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
	}

	/* 链表插入操作,当插入节点和前一个节点连续合并不需要执行操作*/
	if( pxIterator != pxBlockToInsert )
	{
		pxIterator->pxNextFreeBlock = pxBlockToInsert;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FREERTOS源码解析——heap4.c 的相关文章

  • STM32CubeMX+FreeRTOS学习笔记(一)

    嵌入式实时操作系统FreeRTOS 基本概述 在嵌入式领域当中 实时操作系统的应用越来越广泛了 目前嵌入式操作系统种类很多 例如 Clinux C OS II C OS III FreeRTOS RT Thread等等 这篇文章所记录的就是
  • FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)

    我们在前面单独介绍过FreeRTOS的任务通知和消息队列 但是在FreeRTOS中任务间的通讯还有信号量 邮箱 事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析 增加邮箱部分 任务通知发送消息 Demo 202
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系
  • ASCII表

    http office microsoft com zh cn assistance HA011331362052 aspx ASCII 打印字符 数字 32 126 分配给了能在键盘上找到的字符 当您查看或打印文档时就会出现 数字 127
  • 解决错误“ #error “include FreeRTOS.h“ must appear in source files before “include event_groups.““例子分享

    今天来给大家分享一下 关于之前自己在学习FreeRTOS过程中遇到的一个错误提示 话不多说 我们直接来看 错误分析 首先 我们看一下错误的提示 error 35 error directive include FreeRTOS h must
  • 【FreeRTOS】多任务创建

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • freeRTOS手册 第六章 . 中断管理

    如果我对本翻译内容享有所有权 允许任何人复制使用本文章 不会收取任何费用 如有平台向你收取费用与本人无任何关系 第六章 中断管理 章节介绍和范围 事件 嵌入式实时系统必需对环境中的事件做出响应 比如 外部网络设备收到一个发送给TCP IP栈
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的borrowObject方法

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系
  • 【FreeRTOS】任务通知的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • STM32F103移植FreeRTOS必须搞明白的系列知识---2(FreeRTOS任务优先级)

    STM32F103移植FreeRTOS必须搞明白的系列知识 1 Cortex CM3中断优先级 STM32F103移植FreeRTOS必须搞明白的系列知识 2 FreeRTOS任务优先级 STM32F103移植FreeRTOS必须搞明白的系
  • Arduino IDE将FreeRTOS用于STM32

    介绍 适用于STM32F103C8的FreeRTOS STM32F103C是一种能够使用FreeRTOS的ARM Cortex M3处理器 我们直接在Arduino IDE中开始使用STM32F103C8的FreeRTOS 我们也可以使用K
  • Hashmap源码详解

    在开发中的对于数据结构如何选 我们要知道各个数据结构的优缺点 数组 采用一段连续的存储单元来存储数据 对于指定下标的查找 时间复杂度为O 1 但在数组中间以及头部插入数据时 需要复制移动后面的元素O n 优点 查找快 缺点插入慢 链表 一种
  • RT-Thread记录(五、RT-Thread 临界区保护与FreeRTOS的比较)

    本文聊聊临界区 以及RT Thread对临界区的处理 通过源码分析一下 RT Thread 对临界区保护的实现以及与 FreeRTOS 处理的不同 目录 前言 一 临界区 1 1 什么是临界区 1 2 RTOS中的临界区 二 RT Thre
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • FreeRTOS笔记(二)

    FreeRTOS笔记 二 静态任务 文章目录 FreeRTOS笔记 二 静态任务 一 任务定义 二 任务创建 2 1 定义任务栈 2 2 定义任务函数 2 3 定义任务控制块 2 4 实现任务创建函数 三 实现就绪列表 3 1 定义就绪列表
  • STM32 Freertos 添加 外部sram heap_5.c

    1 添加外部SRAM 初始化 2 添加heap 5 c 3 初始化heap 5 c 外部堆栈 Define the start address and size of the two RAM regions not used by the
  • FreeRTOS实时操作系统(三)任务挂起与恢复

    系列文章 FreeRTOS实时操作系统 一 RTOS的基本概念 FreeRTOS实时操作系统 二 任务创建与任务删除 HAL库 FreeRTOS实时操作系统 三 任务挂起与恢复 FreeRTOS实时操作系统 四 中断任务管理 FreeRTO
  • 使用 GCC 编译器的 ARM 内核的堆栈回溯(当存在 MSP 到 PSP 切换时)

    核心 ARM Cortex M4 编译器 GCC 5 3 0 ARM EABI 操作系统 免费 RTOS 我正在使用 gcc 库函数 Unwind Reason Code Unwind Backtrace Unwind Trace Fn v
  • 防止GCC LTO删除函数

    我使用 GCC ARM Embedded 和 FreeRTOS FreeRTOS具有的功能vTaskSwitchContext 仅在某些情况下使用 内联汇编代码 问题是 当我使用LTO时 GCC不考虑内联汇编代码并认为该函数没有被使用 因此

随机推荐

  • 51单片机 | 点亮第一个LED | LED 闪烁实验 | LED流水灯实验

    文章目录 一 51单片机GPIO介绍1 GPIO概念2 GPIO 结构框图与工作原理2 1 P0端口2 2 P1端口2 3 P2端口2 4 P3端口 2 5 要点 二 LED简介三 硬件设计四 软件设计1 点亮第一个LED2 LED 闪烁实
  • matlab二维矩阵可视化几种方法

    目录 一 pcolor 二 imagesc 三 spy 四 文末彩蛋 一 pcolor 以一个100x100块对角矩阵B为例 1 xff1a 原生pcolor 可以在矩阵维度不大时进行可视化 xff0c 带小方格 矩阵比较大时画出整体为黑色
  • 【ROS入门】双系统安装和ros安装踩坑

    记录一下安装Ubuntu双系统和和ros过程中踩的坑 xff0c 防止下次再犯错 一 双系统安装与删除 双系统的安装比一开始想的说实话要简单得多 xff0c 随便用u盘制作一个系统安装盘 xff0c 或者在u盘里面放需要的Ubuntu镜像
  • 【ROS入门】TF与URDF

    一 什么是TF TF全程就是transform xff0c 就是一个坐标系的转换 在ROS中坐标的转换是一个很重要的内容 xff0c 主要还是因为机器的不灵活性 xff0c 如果是人 xff0c 完全可以灵活地控制手臂去抓取一个物体 xff
  • 树莓派3B+——系统安装及显示

    树莓派3B 43 系统安装及显示 目录 树莓派3B 43 系统安装及显示系统安装显示显示器显示3 5寸显示屏显示pc显示 树莓派3B 43 系统安装及显示 初次接触树莓派 xff0c 花了差不多一天的时间把系统的安装和显示全部搞定 xff0
  • VSLAM 相关知识点总结

    VSLAM 相关知识点 这篇文章是对VSLAM涉及的知识点进行系统性的总结和更新 xff0c 一些内容来源至VSLAM经典教材 xff0c 博客 xff0c 和开源项目 引用材料如下表 SLAM十四讲高博古月老师的技术博客崔神的github
  • Windows下CLion中文乱码最有效的解决方式

    作者开发环境 Windows 10 Clion 18 3 MinGW W64 很多人都遇到了Clion的中文乱码问题 xff0c 然后在CSDN上面寻找解决办法 比如这篇很有代表性https blog csdn net Cbk XLL ar
  • Neutron复盘及学习笔记

    前言 对于openstack neutron xff0c 曾花费很多的时间去看它的源码 xff0c 结果啥都没有看出来 openstack代码风格是 xff0c 为了实现plugin的可插拔 xff0c 运用了很多设计模式 xff0c 设计
  • 匿名飞控底层PID代码解析

    大半年就快过去了 大二上学期转瞬即逝 xff0c 离上一次博客也有一段时间了 xff0c 没错 xff0c 庆幸的是我又回来了 xff0c 其实本来第二篇博客是想着写一下两轮平衡车来着 xff0c 代码早就写好了 xff0c 但是硬件不给力
  • IAR、KEIL常见编译报错警告的解决方法(持续更新中)

    Warning Pe550 variable 变量 was set but never used 原因 xff1a 变量定义赋值了但从未使用 解决 xff1a 变量定义删了就可以 xff0c 编译器也不会真的给这个变量分配空间 Warnin
  • VS2019 未能正确加载”balabal.........Package“

    修复VS2019不能自动补全Unity脚本代码时 xff0c 更新了下面这个东西 导致一直蹦下面这些警告 嘣 嘣 嘣 嘣 嘣 网上有很多博客说重置用户数据 禁用Live Share 分析Log信息等等 xff0c 最后看到一个博文说检查更新
  • 51单片机 | 蜂鸣器实验

    文章目录 一 蜂鸣器介绍二 硬件设计三 软件设计四 实验现象 通过单片机的一个 IO 口控制 板载无源蜂鸣器 xff0c 实现蜂鸣器控制 一 蜂鸣器介绍 蜂鸣器是一种一体化结构的电子讯响器 xff0c 采用直流电压供电 xff0c 广泛应用
  • Vue 中WebSocket的使用

    span class token function data span span class token punctuation span span class token punctuation span span class token
  • npm版本与node版本不对应

    npm与node版本不对应 导致无法npm i 报错提示 WARNING You are likely using a version of node tar or npm that is incompatible with this ve
  • 吴恩达阅读文献步骤

    接下来 xff0c 就集中介绍一下如何研究一篇论文 吴恩达认为 xff0c 要理解一篇论文 xff0c 一次将一篇论文从第一个字读到最后一个字 xff0c 可能并不是最佳方式 正确的打开方式是 xff0c 一篇论文至少要看三遍 第一遍 xf
  • 更新docker

    以下是做为前端开发如何更新docker xff0c 萌新操作 1 xff0c 使用工具Finalshell xff0c 连接到ssh 2 xff0c 连接到服务器 xff0c 选择ssh xff0c 并配置服务名称 主机 服务地址 xff0
  • 计算机视觉理论笔记 (11) - 运动估计 (Motion Estimation)

    运动估计 光流法 Optical Flow 限制Horn xff06 Schunck 全局方法 global method 光流法约束等式 Constrain Equation 小幅度动作问题全局方法和局部方法的比较全局方法局部方法 基于块
  • 嵌入式,面试题目网页搜集

    指针笔试题 嵌入式面试
  • Java第一天笔记02——eclipse的常用快捷键

    1 main方法 main 43 Alt 43 2 System out println syso 43 Alt 43 3 批量修改 Alt 43 Shift 43 R 4 for for 43 Alt 43 5 代码格式化 Ctrl 43
  • FREERTOS源码解析——heap4.c

    目录 内存管理heap4无法解决的内存碎片 xff1a HEAP4简析分配内存在哪 xff0c 大小多少如何管理 重要源码解析 内存管理 freertos目前提供了以下5种内存管理 xff0c 特点如下 heap1 xff1a 最简单的内存