一.动态内存堆的使用
RT-thread中有两套使用内存的方法——动态内存堆和内存池,这里先介绍一下动态内存堆的使用,内存池的使用最后一节也会介绍
- 申请内存:
void *rt_malloc(rt_size_t size)
如果申请成功返回指针,申请失败,返回常量RT_NULL
- 释放内存:
void rt_free(void *rmem)
- 批量设置内存数据:
void *rt_memset(void *s, int c, rt_ubase_t count)
- 在已分配内存的基础上,重新分配内存块的大小(增加或减少),内存原数据不变,如果减少,会截断原内存
void *rt_realloc(void *rmem, rt_size_t newsize)
二.创建线程(任务)
- 静态创建线程
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
- 动态创建线程
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
- 启动线程
rt_err_t rt_thread_startup(rt_thread_t thread)
注意:静态创建线程需要指定任务栈的起始地址以及传入任务控制块参数,而动态创建线程不需要考虑两者,并且动态创建线程的返回值是线程控制块
一个任务被创建之后,需要调用rt_thread_startup才会使这个任务进入就绪列表(开始被任务调度器调度)
三.延时函数
- 第一个延时函数很简单,原型如下:
rt_err_t rt_thread_delay(rt_tick_t tick)//延时指定时钟节拍
延时期间,任务挂起,延时结束,任务进入就绪态。还有一个rt_thread_sleep与此函数完全等效
- 第二个延时函数做了一些细节处理:原型如下:
rt_err_t rt_thread_mdelay(rt_int32_t ms)
毫秒级延时,延时期间,任务挂起,延时结束,任务进入就绪态。
由于系统节拍精度限制,延时误差在1个系统节拍之内。例如,系统节拍周期是10ms,则指定延时502ms,实际上会延时510ms,因为多出来的2ms仍然被系统认为差一个系统节拍
- 微秒级延时
一些对时间要求非常严格的时候(比如IIC时序),可能需要把延时时间精确到us
void rt_hw_us_delay(rt_uint32_t us)
四 .GPIO驱动架构驱动IO
#include <rtdevice.h>//使用GPIO驱动框架必须包含该头文件
- IO初始化:
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
对stm32而言,参数二相关宏定义如下
#define PIN_MODE_OUTPUT 0x00
#define PIN_MODE_INPUT 0x01
#define PIN_MODE_INPUT_PULLUP 0x02
#define PIN_MODE_INPUT_PULLDOWN 0x03
#define PIN_MODE_OUTPUT_OD 0x04
- 写IO
void rt_pin_write(rt_base_t pin, rt_base_t value)
参数一索引表在drv_gpio.c文件中,参数二相关宏定义如下
#define PIN_LOW 0x00
#define PIN_HIGH 0x01
- 读IO
int rt_pin_read(rt_base_t pin)
返回值参考rt_pin_write参数二
五.关于线程栈合适大小的确定
在命令行调用list_thread命令打印出结果如下:
msh >list_thread
thread pri status sp stack size max used left tick error
------ --- ------- ---------- ---------- ------ ---------- ---
tshell 20 ready 0x00000080 0x00001000 07% 0x00000005 000
tidle 31 ready 0x00000040 0x00000100 34% 0x00000009 000
第一列:任务名称
第二列:任务优先级
第三列:任务状态
第四列:sp指针
第五列:任务栈大小
第六列:任务在运行过程中占用任务栈的最大比例
第七列:不知道(xix)
第八列:不知道(xixi)
这里重点关注第六列这个数,它反应了任务运行内存占用任务创建时分配的任务栈的最大比例,所以它越小就表明任务栈的浪费越严重,需要把任务栈分配的再小一些,这个比例在70%左右最为稳妥!
六.空闲线程钩子函数
空闲线程是一个状态永远为就绪状态的最低优先级线程。也会负责一些系统资源回收的工作。空闲线程的性质决定只有在其他任务都休眠了之后才会执行它,具体执行哪个函数呢,答案是----钩子函数
- 设置空闲线程钩子函数:
rt_err_t rt_thread_idle_sethook(void (*hook)(void))
- 删除空闲线程钩子函数:
rt_err_t rt_thread_idle_delhook(void (*hook)(void))
设置钩子函数之后,在空闲线程中会调用设置的这些钩子函数,没错,是这些!意味着你可以定义多个钩子函数。钩子函数和空闲线程的关系就像是线程代码(入口函数)与线程之间的关系,只不过空闲线程可以同时拥有多个钩子函数(入口函数)。并且空闲线程会按照顺寻循环调用他们,也可以随时在某个进程中删除这些钩子函数。
钩子函数的意义常常是用来做一些无关紧要的事情。因为空闲线程的优先级最低,所以只有在较高优先级的线程都休眠了之后,才会调用钩子函数!
除此之外,还需注意,空闲线程是一个状态永远为就绪状态的最低优先级线程。所以钩子函数必须保证任何时候都不会导致空闲线程挂起阻塞!
七.系统调度钩子函数
当系统进行任务切换的时候,也可以设置钩子函数,可以使用下面的API来设置哪=指定两个任务切换时的钩子函数
rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
八.临界区保护
保护共享资源的一些常用措施如下:禁止调度,信号量,互斥量,临界区等,下面介绍一下禁止调度(临界区),关闭中断在rt-thread系统中的API函数
- 进入临界区
void rt_enter_critical(void)
进入临界区之后,任务调度器关闭,禁止任务切换,中断依旧可以响应
- 退出临界区
void rt_exit_critical(void)
- 关闭中断:
线程调度是依靠中断来进行的,因此如果把中断函数关闭会直接导致线程无法调度,进而导致任务无法切换,比临界区更强大的是,关闭中断在禁止任务切换的同时也禁止中断响应了
rt_base_t level;
level = rt_hw_interrupt_disable();//关闭中断,返回当前中断状态给level
/*临界区*/
/*。。。。。。*/
rt_hw_interrupt_enable(level);//开启中断,恢复中断状态到level记录的状态
九.信号量
- 静态信号量的初始化(创建):
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
参数一:rt_sem_t其实就是struct rt_semaphore的指针类型
参数二:信号量的名字
参数三:信号量的初始值
参数四:信号量标志,可以是以下几个值:
#define RT_IPC_FLAG_FIFO 0x00 //先进先出获取信号量
#define RT_IPC_FLAG_PRIO 0x01 //按照优先级顺寻
- 静态信号量脱离(删除)
rt_err_t rt_sem_detach(rt_sem_t sem)
- 动态信号量创建
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
如果返回的指针不为空,则说明创建信号量成功
- 动态信号量删除
rt_err_t rt_sem_delete(rt_sem_t sem)
- 获取信号量
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
参数一:信号量指针类型
参数二:超时时间,可以指定为以下几个宏定义或者自己指定值(单位是时间片)
返回值:获取成功返回RT_EOK,获取失败(是指在指定超时时间之内未获取到),返回其他
#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
- 获取信号量(非阻塞)
rt_err_t rt_sem_trytake(rt_sem_t sem)
- 释放信号量
rt_err_t rt_sem_release(rt_sem_t sem)
信号量的应用:
信号量初次接触之后,我还不知道它究竟有什么用,感觉也没有互斥量有用啊,在知识层面上只是为了向互斥量过度,我刚开始其实是这么认为的。但是看了rt-thread官方的消费者生产者模型之后,大概有了一个对信号量应用的初步认识了
官方的生产者消费者模型如下所示:
代码层面的实现如下:(以下代码来自rt官方)
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-08-24 yangjie the first version
*/
/*
* 程序清单:生产者消费者例子
*
* 这个例子中将创建两个线程用于实现生产者消费者问题
*(1)生产者线程将cnt值每次加1并循环存入array数组的5个成员内;
*(2)消费者线程将生产者中生产的数值打印出来,并累加求和
*/
#include <rtthread.h>
#define THREAD_PRIORITY 6
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 定义最大5个元素能够被产生 */
#define MAXSEM 5
/* 用于放置生产的整数数组 */
rt_uint32_t array[MAXSEM];
/* 指向生产者、消费者在array数组中的读写位置 */
static rt_uint32_t set, get;
/* 指向线程控制块的指针 */
static rt_thread_t producer_tid = RT_NULL;
static rt_thread_t consumer_tid = RT_NULL;
struct rt_semaphore sem_lock;
struct rt_semaphore sem_empty, sem_full;
/* 生产者线程入口 */
void producer_thread_entry(void *parameter)
{
int cnt = 0;
/* 运行10次 */
while (cnt < 10)
{
/* 获取一个空位 */
rt_sem_take(&sem_empty, RT_WAITING_FOREVER);
/* 修改array内容,上锁 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
array[set % MAXSEM] = cnt + 1;
rt_kprintf("the producer generates a number: %d\n", array[set % MAXSEM]);
set++;
rt_sem_release(&sem_lock);
/* 发布一个满位 */
rt_sem_release(&sem_full);
cnt++;
/* 暂停一段时间 */
rt_thread_mdelay(20);
}
rt_kprintf("the producer exit!\n");
}
/* 消费者线程入口 */
void consumer_thread_entry(void *parameter)
{
rt_uint32_t sum = 0;
while (1)
{
/* 获取一个满位 */
rt_sem_take(&sem_full, RT_WAITING_FOREVER);
/* 临界区,上锁进行操作 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
sum += array[get % MAXSEM];
rt_kprintf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]);
get++;
rt_sem_release(&sem_lock);
/* 释放一个空位 */
rt_sem_release(&sem_empty);
/* 生产者生产到10个数目,停止,消费者线程相应停止 */
if (get == 10) break;
/* 暂停一小会时间 */
rt_thread_mdelay(50);
}
rt_kprintf("the consumer sum is: %d\n", sum);
rt_kprintf("the consumer exit!\n");
}
int producer_consumer(void)
{
set = 0;
get = 0;
/* 初始化3个信号量 */
rt_sem_init(&sem_lock, "lock", 1, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_empty, "empty", MAXSEM, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_full, "full", 0, RT_IPC_FLAG_FIFO);
/* 创建生产者线程 */
producer_tid = rt_thread_create("producer",
producer_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
if (producer_tid != RT_NULL)
{
rt_thread_startup(producer_tid);
}
else
{
rt_kprintf("create thread producer failed");
return -1;
}
/* 创建消费者线程 */
consumer_tid = rt_thread_create("consumer",
consumer_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (consumer_tid != RT_NULL)
{
rt_thread_startup(consumer_tid);
}
else
{
rt_kprintf("create thread consumer failed");
return -1;
}
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(producer_consumer, producer_consumer sample);
十.互斥量
- 定义动态互斥量(指针)
rt_mutex_t dynamic_mutex = RT_NULL;
- 定义静态互斥量
struct rt_mutex static_mutex;
与之前的几个变量相同,动态方式定义的互斥量是静态方式定义互斥量的指针而已,互斥量结构体如下:
struct rt_mutex
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< 互斥量的值只有开锁和上锁两种零态 */
rt_uint8_t original_priority; /**< 拥有互斥量线程的优先级,这个成员是为避免优先级反转而设计的x */
rt_uint8_t hold; /**< numbers of thread hold the mutex */
struct rt_thread *owner; /**<指向当前持有互斥量的线程 */
};
typedef struct rt_mutex *rt_mutex_t;
- 初始化(静态方式创建):
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
flag参数参考信号量初始化
- 脱离(静态方式删除)
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
- 创建(动态)
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
- 删除(动态)
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
- 获取互斥量(上锁)
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
参数二:超时时间,单位是时钟节拍,也可以为以下两个宏定义
#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
- 释放互斥量(开锁)
rt_err_t rt_mutex_release(rt_mutex_t mutex)
- 互斥量使用注意事项:
注意:互斥量不能在中断中使用!只能在线程中使用。在上锁期间,线程可以进行延时操作,但是尽量避免,以保证其他线程可以及时使用共享资源。
- 互斥量是如何解决优先级翻转问题的?
互斥锁的一大优点就是可以通过优先级继承算法来解决二值信号量导致的优先级翻转问题,所谓优先级翻转,rtt官方的解释如下:
当一个高优先级线程试图通过某种互斥IPC对象机制(其实就是二至信号量)访问共享资源时,如果该IPC对象已经被一低优先级的线程所持有,而这个低优先级线程在运行过程中可能由被其他一些中等优先级的线程抢占,因此造成高优先级线程被许多中等优先级的线程阻塞的情况。这就称为优先级反转。
我来解读一下:
因为此时高优先级的任务在阻塞等待互斥IPC对象开锁,而开锁需要此时持有锁的低优先级的任务去开锁,但是此时低优先级的任务被中等优先级的任务阻塞,因此整体的效果就是高优先级的任务阻塞等待开锁,中等优先级的任务一直在运行。
互斥量(锁)如何解决优先级翻转对实时性的影响呢?
互斥锁就是为了解决二值信号量这种可能导致的优先级反转的情况,具体是怎么解决呢?当互斥锁上锁的时候,被上锁线程的优先级被设置为所有等待互斥锁的优先级中最高的优先级,当释放锁之后,优先级恢复原状。这样可以有效避免低优先级线程在上锁期间被中等优先级的线程抢占,优先级翻转的问题得以解决
十一.事件集
- 数据结构
struct rt_event
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint32_t set; /**< event set */
};
- 定义静态事件集
struct rt_event static_evt;
- 定义动态事件集
rt_event_t dynamic_evt;
- 静态创建
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
- 静态删除:
rt_err_t rt_event_detach(rt_event_t event)
- 动态创建:
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
动态删除:
rt_err_t rt_event_delete(rt_event_t event)
- 发送事件:
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
发送事件,其实就是设置事件集中的set变量,例如,想要把第0个事件置1,就设置set为0x01,想要清零所有事件集,就设置set为0x00
- 接收事件:
rt_err_t rt_event_recv(rt_event_t event,
rt_uint32_t set,
rt_uint8_t option,
rt_int32_t timeout,
rt_uint32_t *recved)
返回值:接收成功返回RT_EOK
参数二和参数三需要配合使用,参数二指定对事件集的哪几位感兴趣,参数三指定以什么方式来接收感兴趣的这几个事件,可以是与的方式(只有感兴趣的所有事件都置位,才算等待成功)也可以是或的方式(只要感兴趣的事件有一个置位,就算等待成功),这两个方式只能同时选择一个。除了他们两个之外,还有一个方式是这样的功能:当等待成功之后,清除本次调用函数所有的感兴趣事件,这三个方式的宏定义如下:
#define RT_EVENT_FLAG_AND 0x01 /**< logic and */
#define RT_EVENT_FLAG_OR 0x02 /**< logic or */
#define RT_EVENT_FLAG_CLEAR 0x04 /**< clear flag */
参数五是给定一个指针,当成功等待到事件之后,把等待到的事件按位返回给这个指针,没有什么实际意义,不需要关注,直接给RT_NULL即可
十二.邮箱
- 数据结构
struct rt_mailbox
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint32_t *msg_pool; /**< start address of message buffer */
rt_uint16_t size; /**< size of message pool */
rt_uint16_t entry; /**< index of messages in msg_pool */
rt_uint16_t in_offset; /**< input offset of the message buffer */
rt_uint16_t out_offset; /**< output offset of the message buffer */
rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox */
};
typedef struct rt_mailbox *rt_mailbox_t;
- 定义静态邮箱
struct rt_mailbox static_mailbox;
- 定义动态邮箱
rt_mailbox_t dynamic_mailbox;
- 静态创建
rt_err_t rt_mb_init(rt_mailbox_t mb,
const char *name,
void *msgpool,
rt_size_t size,
rt_uint8_t flag)
参数三:邮箱缓冲区,静态创建需要自行指定
参数四:指定邮箱中最多可以存储多少个邮件
- 静态删除
rt_err_t rt_mb_detach(rt_mailbox_t mb)
- 动态创建
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
- 动态删除
rt_err_t rt_mb_delete(rt_mailbox_t mb)
- 非阻塞发送邮件(发送之后,不必等待邮件被接收)
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value)
发送成功返回RT_EOK,否则表示邮箱满了,发送不了新邮箱了
- 阻塞发送邮件(如果邮箱满了,则等待指定的时间,如果在这段时间内邮箱有空位,则发送,超时返回非RT_EOK的值)
rt_err_t rt_mb_send_wait(rt_mailbox_t mb,
rt_uint32_t value,
rt_int32_t timeout)
关于发送邮箱的一个技巧,发送函数的参数二只有32位,如果想发送更多内容,如,发送一个很长的字符串怎么办呢?答案是,发送字符串的指针,可以把指针强转成32位数据发送出去,接收线程接收到之后,再把数据转化成指针即可获取指针指向的数据
- 接收邮件(接收邮箱中最旧的那个邮件)
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout)
接收成功返回RT_EOK
十三.消息队列
数据结构
struct rt_messagequeue
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
void *msg_pool; /**< start address of message queue */
rt_uint16_t msg_size; /**< message size of each message */
rt_uint16_t max_msgs; /**< max number of messages */
rt_uint16_t entry; /**< index of messages in the queue */
void *msg_queue_head; /**< list head */
void *msg_queue_tail; /**< list tail */
void *msg_queue_free; /**< pointer indicated the free node of queue */
};
- 静态定义
struct rt_messagequeue static_msq;
- 动态定义
rt_mq_t dynamic_mq;
- 静态创建
rt_err_t rt_mq_init(rt_mq_t mq,
const char *name,
void *msgpool,
rt_size_t msg_size,
rt_size_t pool_size,
rt_uint8_t flag)
参数三:消息内存池,用于存放消息链表
参数四:消息的大小(单位字节)
参数五:消息内存池大小
- 静态删除
rt_err_t rt_mq_detach(rt_mq_t mq)
- 动态创建
rt_mq_t rt_mq_create(const char *name,
rt_size_t msg_size,
rt_size_t max_msgs,
rt_uint8_t flag)
参数二:消息大小(单位字节)
参数三:消息队列最大可容纳消息个数
- 动态删除
rt_err_t rt_mq_delete(rt_mq_t mq)
- 发送消息
rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size)
参数二:要发送的消息缓存数字指针
参数三:要发送消息内容大小
- 加急发送消息(接收消息默认接收最旧的消息,加急发送可以直接把要发送的消息转为最旧的消息)
rt_err_t rt_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size)
- 接收消息
rt_err_t rt_mq_recv(rt_mq_t mq,
void *buffer,
rt_size_t size,
rt_int32_t timeout)
参数二:接收消息缓存数字指针
参数三:要接受的消息大小(不能大于消息大小),也就是说,接收到一个消息,可以选择只接受前边的几个字节
十四.软件定时器
软件定时器有两种模式供用户使用----HARDTIMER模式与SOFTTIMER模式
官方对它的两个模式的解释如下:
HARDTIMER模式的定时器超时函数在中断上下文中执行,此模式在定时器初始化时指定。在中断上下文环境中执行时,对于超时函数的要求与中断服务例程的要求相同,执行时间应该尽量短,执行时不应导致当前上下文挂起。HARDTIMER模式是RT-Thread软件定时器的默认模式
SOFTTIMER模式的定时器超时函数在系统的timer线程上下文中执行。通过宏定义RT_USING_TIMER_SOFT来决定是否启用该模式。当启用SOFTTIMER模式后,我们可以在定时器初始化时指定定时器工作在SOFTTIMER模式
- 软件定时器数据结构
struct rt_timer
{
struct rt_object parent; /**< inherit from rt_object */
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];
void (*timeout_func)(void *parameter); /**< timeout function */
void *parameter; /**< timeout function's parameter */
rt_tick_t init_tick; /**< timer timeout tick */
rt_tick_t timeout_tick; /**< timeout tick */
};
typedef struct rt_timer *rt_timer_t;
- 静态定义
struct rt_timer static_timer;
- 动态定义
rt_timer_t dynamic_timer;
- 静态创建软件定时器
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
参数三:软件定时器回调函数指针,用户自定义回调函数
参数四:软件定时器回调函数参数
参数五:指定软件定时器定时时间(单位为系统时钟节拍)
参数六可以是以下几个值:
#define RT_TIMER_FLAG_ONE_SHOT 0x0 /**< 单次定时 默认是这个模式 */
#define RT_TIMER_FLAG_PERIODIC 0x2 /**< 周期定时 */
#define RT_TIMER_FLAG_HARD_TIMER 0x0 /**< HARDTIMER模式,默认就是该模式*/
#define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**< SOFTTIMER模式,需要开启宏RT_USING_TIMER_SOFT才可以使用该模式 */
- 静态删除软件定时器
rt_err_t rt_timer_detach(rt_timer_t timer)
- 动态创建软件定时器
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
- 动态删除软件定时器
rt_err_t rt_timer_delete(rt_timer_t timer)
- 启动定时器
rt_err_t rt_timer_start(rt_timer_t timer)
停止定时器
rt_err_t rt_timer_stop(rt_timer_t timer)
十五.RT-Thread内存池
第一节介绍的动态内存堆的使用有两个缺点,第一,效率低,因为每次都要查找空闲内存;第二,容易产生内存碎片,导致大量内存浪费。这也是灵活申请的代价。因此,RT-Thread引入内存池来解决这两个缺点。
RT-Thread的内存池还支持线程挂起功能,即当内存池没有空闲内存时,一个线程试图申请内存池的存,则这个线程会被挂起,当有空闲内存或者超市之后,线程被再次唤醒
RT-Thread的内存池的缺点是只能一块一块申请,而不能像动态内存堆那样申请任意个数的字节
- 内存池数据结构
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 */
};
typedef struct rt_mempool *rt_mp_t;
- 静态定义
struct rt_mempool static_memp;
- 动态定义
rt_mp_t dynamic_memp;
- 静态创建
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_mp_t 类型
- 动态删除
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)
完结撒花!!