Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
内核学习营
RT-Thread--内核学习mempool(六)
发布于 2018-10-04 13:08:30 浏览:2430
订阅该版
* 本帖最后由 yushigengyu 于 2018-10-4 13:11 编辑 * 今天来看看RTT中的内存分配。关于内存分配,RTT官网提供了十分完整的算法说明。 由于RTT是实时操作系统,其对时间有着严格的要求,内存分配的时间往往要比通用操作系统要更苛刻。 * 首先,分配内存的时间必须是确定的。一般内存管理算法是根据需要存储的数据的长度在内存中去寻找一个与这段数据相适应的空闲内存块,然后将数据存储在里面。**而寻找这样一个空闲内存块所耗费的时间是不确定的**,这对于实时系统来说,**是不可接受的,实时系统必须要保证内存块的分配过程在可预测的确定时间内完成,否则实时任务对外部事件的响应也将变得不可确定**。 * 其次,随着内存不断被分配和释放,整个内存区域会产生越来越多的碎片(因为在使用过程中,申请了一些内存,其中一些释放了,导致内存空间中存在一些小的内存块,它们地址不连续,不能够作为一整块的大内存分配出去),系统中还有足够的空闲内存,但因为它们地址并非连续,不能组成一块连续的完整内存块,会使得程序不能申请到大的内存。对于通用系统而言,这种不恰当的内存分配算法可以通过重新启动系统来解决(每个月或者数个月进行一次),但是对于那些需要常年不间断地工作于野外的嵌入式系统来说,就变得让人无法接受了。**所以实时操作系统的内存分配算法应该要尽可能妥善的改进碎片问题**。 * 最后,嵌入式系统的资源环境也是不尽相同,有些系统的资源比较紧张,只有数十KB的内存可供分配,而有些系统则存在数MB的内存,如何为这些不同的系统,选择适合它们的高效率的内存分配算法,就将变得复杂化。**所以实时操作系统的内存分配算法要尽可能多的适应内存不等的各种平台**。 RTT操作系统在内存管理上,针对以上问题,提供了不同的内存分配算法。 大体上可分为两类:静态分区内存管理与动态内存管理,而动态内存管理又根据可用内存的多少划分为两种情况:一种是针对小内存块的分配管理(小内存管理算法),另一种是针对大内存块的分配管理(SLAB管理算法)。 这里先来看看其提供的一种名为**内存池**(Memory Pool)的内存分配管理算法,内存池是一种用于分配大量大小相同的小对象的技术。它可以极大加快内存分配/释放的速度。 [align=center] ![987,551](https://www.rt-thread.org/document/site/submodules/rtthread-manual-doc/figures/rt_mem_pool.png) 如上图所示,内存池一旦初始化完成,内部的内存块大小将不能再做调整。每一个内存池其实就是一个链表,由于链表的大小全部相同,每次分配的时候,从链表中取出链头上第一个内存块,提供给申请者,每次释放内存,就把释放的内存重新加入链表即可。这种算法的优势是显而易见的,释放和分配内存都只需要O(1)的时间即可完成。当然也有很大的缺陷,只能分配固定的内存,对于不同大小的内存分配无法很好的满足。 下面就来看看rt_mempool这个类的相关成员: ```struct rt_mempool { struct rt_object parent; /**< inherit from rt_object */ void *start_address; /**< memory pool start */ rt_size_t size; /**< size of memory pool */ rt_size_t block_size; /**< size of memory blocks */ rt_uint8_t *block_list; /**< memory blocks list */ rt_size_t block_total_count; /**< numbers of memory block */ rt_size_t block_free_count; /**< numbers of free memory block */ rt_list_t suspend_thread; /**< threads pended on this resource */ rt_size_t suspend_thread_count; /**< numbers of thread pended on this resource */ };``` 1.parent rt_object实例化,同样rt_mempool也是继承自rt_object 2.start_address 内存池起始地址 3.size 内存池总大小,size=(block_size + sizeof(uint8_t *)) * block_total_count 4.block_size 每个块的大小 5.block_list 空闲块所组成的列表 6.block_total_count 总内存块数量 7.block_free_count 空闲内存块数量 8.suspend_thread 由于等待空闲内存而挂起的线程列表 9.suspend_thread_count 挂起的线程总数 内存池内存分配算法相对来说比较简单,相关的函数如下: ```rt_err_t rt_mp_init(struct rt_mempool *mp, const char *name, void *start, rt_size_t size, rt_size_t block_size); rt_err_t rt_mp_detach(struct rt_mempool *mp); rt_mp_t rt_mp_create(const char *name, rt_size_t block_count, rt_size_t block_size); rt_err_t rt_mp_delete(rt_mp_t mp); void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time); void rt_mp_free(void *block);``` 一、rt_mp_init与rt_mp_create ```rt_mp_t rt_mp_create(const char *name, rt_size_t block_count, rt_size_t block_size) { rt_uint8_t *block_ptr; struct rt_mempool *mp; register rt_size_t offset; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name); /* allocate object failed */ if (mp == RT_NULL) return RT_NULL; /* initialize memory pool */ block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); mp->block_size = block_size; mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count; /* allocate memory */ mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) * block_count); if (mp->start_address == RT_NULL) { /* no memory, delete memory pool object */ rt_object_delete(&(mp->parent)); return RT_NULL; } mp->block_total_count = block_count; mp->block_free_count = mp->block_total_count; /* initialize suspended thread list */ rt_list_init(&(mp->suspend_thread)); mp->suspend_thread_count = 0; /* initialize free block list */ block_ptr = (rt_uint8_t *)mp->start_address; for (offset = 0; offset < mp->block_total_count; offset ++) { *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)); } *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) = RT_NULL; mp->block_list = block_ptr; return mp; }``` rt_mp_init和rt_mp_create的区别为,rt_mp_init用来初始化所需的内存块已经提前分配好的内存池,rt_mp_create则需要调用rt_malloc来分配内存池所需内存,所以使用rt_mp_create一定要其他内存分配方法配合才能进行。 rt_mp_create功能: 1.初始化object基类。 2.按照传入的参数初始化各种成员如: start_address,size,block_size,block_total_count,block_free_count等。 3.通过rt_malloc分配内存池所需的内存。 4.初始化空闲内存链表,将每个空闲的空间通过链表链接起来。 二、rt_mp_delete和rt_mp_detach 两者的功能均为删除内存池,rt_mp_delete和rt_mp_detach的区别为,rt_mp_delete用来删除所需的内存块已经提前分配好的内存池(主要是将其移出内核的对象容器),rt_mp_detach则需要调用rt_free来释放分配给内存池内存 ```rt_err_t rt_mp_delete(rt_mp_t mp) { struct rt_thread *thread; register rt_ubase_t temp; RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(mp != RT_NULL); RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE); /* wake up all suspended threads */ while (!rt_list_isempty(&(mp->suspend_thread))) { /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* get next suspend thread */ thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); /* set error code to RT_ERROR */ thread->error = -RT_ERROR; /* * resume thread * In rt_thread_resume function, it will remove current thread from * suspend list */ rt_thread_resume(thread); /* decrease suspended thread count */ mp->suspend_thread_count --; /* enable interrupt */ rt_hw_interrupt_enable(temp); } /* release allocated room */ rt_free(mp->start_address); /* detach object */ rt_object_delete(&(mp->parent)); return RT_EOK; }``` rt_mp_delete主要功能: 1.唤醒所有被该内存池阻塞的线程 2.释放内存池内存 3.删除基类object 三、rt_mp_alloc和rt_mp_free 使用内存池分配内存的函数,分别为释放和分配。 下面是rt_mp_alloc: ```void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) { rt_uint8_t *block_ptr; register rt_base_t level; struct rt_thread *thread; rt_uint32_t before_sleep = 0; /* get current thread */ thread = rt_thread_self(); /* disable interrupt */ level = rt_hw_interrupt_disable(); while (mp->block_free_count == 0) { /* memory block is unavailable. */ if (time == 0) { /* enable interrupt */ rt_hw_interrupt_enable(level); rt_set_errno(-RT_ETIMEOUT); return RT_NULL; } RT_DEBUG_NOT_IN_INTERRUPT; thread->error = RT_EOK; /* need suspend thread */ rt_thread_suspend(thread); rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); mp->suspend_thread_count++; if (time > 0) { /* get the start tick of timer */ before_sleep = rt_tick_get(); /* init thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &time); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule(); if (thread->error != RT_EOK) return RT_NULL; if (time > 0) { time -= rt_tick_get() - before_sleep; if (time < 0) time = 0; } /* disable interrupt */ level = rt_hw_interrupt_disable(); } /* memory block is available. decrease the free block counter */ mp->block_free_count--; /* get block from block list */ block_ptr = mp->block_list; RT_ASSERT(block_ptr != RT_NULL); /* Setup the next free node. */ mp->block_list = *(rt_uint8_t **)block_ptr; /* point to memory pool */ *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; /* enable interrupt */ rt_hw_interrupt_enable(level); RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook, (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)))); return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)); }``` 主要操作如下: 1.判断是否还有空余的内存块,若没有则把当前线程挂起,加入到内存池挂起列表,并开启线程的定时器,在一定时候后重新唤醒线程。 2.若还有空闲内存块,则返回空闲内存块的地址,并把内存块移除空闲内存列表,同时block_free_count减1. 3.调用rt_mp_alloc_hook钩子函数 接下来是rt_mp_free: ```void rt_mp_free(void *block) { rt_uint8_t **block_ptr; struct rt_mempool *mp; struct rt_thread *thread; register rt_base_t level; /* get the control block of pool which the block belongs to */ block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *)); mp = (struct rt_mempool *)*block_ptr; RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block)); /* disable interrupt */ level = rt_hw_interrupt_disable(); /* increase the free block count */ mp->block_free_count ++; /* link the block into the block list */ *block_ptr = mp->block_list; mp->block_list = (rt_uint8_t *)block_ptr; if (mp->suspend_thread_count > 0) { /* get the suspended thread */ thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); /* set error */ thread->error = RT_EOK; /* resume thread */ rt_thread_resume(thread); /* decrease suspended thread count */ mp->suspend_thread_count --; /* enable interrupt */ rt_hw_interrupt_enable(level); /* do a schedule */ rt_schedule(); return; } /* enable interrupt */ rt_hw_interrupt_enable(level); }``` 主要操作如下: 1.调用rt_mp_free_hook钩子函数。 2.将block_free_count加1,并把释放的内存块重新加入到空闲列表中。 3.检查是否有被其阻塞线程,唤醒线程。 **这里有几个问题,使用free的时候需要注意:** * rt_mp_free没进行参数检查,如果传入了错误的指针,如NULL。可能会引起未知错误。 * 若使用rt_mp_free释放了不是当前mp分配的内存,也会引起同样问题。
查看更多
7
个回答
默认排序
按发布时间排序
whj467467222
认证专家
2018-10-04
开源,分享,交流,共同进步
赞
yushigengyu
认证专家
2018-10-04
这家伙很懒,什么也没写!
>赞 --- 这个论坛只有我们2个人;P
whj467467222
认证专家
2018-10-07
开源,分享,交流,共同进步
>这个论坛只有我们2个人 --- 不是的,估计过节就我们在吧。
yuexiaoban
2018-12-23
这家伙很懒,什么也没写!
请问下,其他的分配方法有说明么
yangjie
认证专家
2018-12-26
hello
>这个论坛只有我们2个人 --- 当然不止你俩了
huabin
2019-09-18
这家伙很懒,什么也没写!
新手前来报道
wh_zhangll
2022-04-01
See through the appearance to perceive the essence.
赞,要是能讲讲rt_mp_init具体分配过程就更赞了
撰写答案
登录
注册新账号
关注者
0
被浏览
2.4k
关于作者
yushigengyu
这家伙很懒,什么也没写!
提问
15
回答
25
被采纳
0
关注TA
发私信
相关问题
1
【内核学习】rtthread内核移植记录-STM32F103ZET6-HAL库
2
《内核学习营》+水一方+自用STM32F103VC 板RT-Thread内核移植分享
3
《内核学习营》+水一方+项目中创建标准的 RT-Thread工程
4
内核学习营+坦然+探索者stm32f407板子RT-thread循环点亮led灯
5
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口字符点灯
6
<内核学习营>+坦然+探索者stm32f407板子RT-thread的pwm点灯实验
7
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口实验
8
<内核学习营>+坦然+野火stm32f103板子RT-thread读写SD卡实验
9
<内核学习营>+坦然+探索者stm32f407板子RT-thread的RTC闹钟实验
10
【内核学习营】+王秀峰+led_rgb
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
2
GD32F450 片内 flash驱动适配
3
STM32H7R7运行CherryUSB
4
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
5
使用RC522软件包驱动FM1722
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部