freertos学习


FreeRTOS 中的 heap 4 内存管理,可以算是 heap 2 的增强版本。
heap2 每次内存分配后都会产生一个内存块,多次分配后,会产生很多内存碎片。

怎么来标记这个块是空闲还是非空闲?
所以就有了 heap 4,它相比 heap 2 来说,提供了相邻空闲的内存块合并的功能,一定程度上减少了内存碎片,使得释放了的内存能够再度合并称为较为大的内存块,以供有大内存块的分配场景使用;


内存管理 heap1

heap1 不提供 Free 内存的接口,也就是说,这套内存管理是只提供的分配,一旦申请成功后,这块内存再也不能被释放;实际上,大多数的嵌入式系统并不需要动态删除任务、信号量、队列等,而是在初始化的时候一次性创建好,便一直使用,永远不用删除。所以这个内存管理策略实现简洁、安全可靠,使用的非常广泛。


内存管理 heap2

heap 1 的内存管理其实只是简单的实现了内存对齐的分配策略,heap 2 的实现策略相比 heap 1 稍微复杂一点,不仅仅是提供了分配内存的接口,同时也提供了释放内存的接口;
heap 2 的内存分配策略中,并没有提供空闲内存的合并策略,对内存碎片没有处理;换句话来说,如果有多次的,大小各异的内存申请和释放的场景下,很可能导致很多内存碎片;
heap 2 可以支持分配和释放,那么管理内存的手段势必比 heap 1 复杂一些,heap 2 对内存进行分块管理,将每块内存通过一个表征该内存块的的数据结构表示,以单向链表串在一起;


内存管理 heap4
所以就有了 heap 4,它相比 heap 2 来说,提供了相邻空闲的内存块合并的功能,一定程度上减少了内存碎片,使得释放了的内存能够再度合并称为较为大的内存块,以供有大内存块的分配场景使用

怎么检测到实际的空间时紧挨着


寄存器组
CM3 处理器拥有 R0~R15 一共 16 个内部寄存器
R0~R12 称之为通用寄存器。
R13 默认情况下被用做堆栈指针;堆栈指针分为 MSP 和 PSP,后面会详细描述;
R14 默认情况作为 LR,也就是链接寄存器,当程序调用其他函数后,此寄存器保存了返回地址,使得子程序执行完毕后,得以返回;
R15 默认作为 PC 指针;

CM3 中,除了上述 16 个寄存器以外,还有几个特殊的寄存器组:
xPSR:状态寄存器;
PRIMASK:  中断屏蔽寄存器;
FAULTMASK:中断屏蔽寄存器;
BASEPRI:中断屏蔽寄存器,按照优先级进行屏蔽;
CONTROL:处理器模式和堆栈选择;


xPSR 是 Program Status Register 程序状态寄存器的意思,前面有个 x 代表他是由 3 个小的寄存器构成:
APSR:应用程序状态寄存器;
IPSR:中断程序状态寄存器;
EPSR:执行程序状态寄存器;

2.2、PRIMASK  中断屏蔽寄存器
这个是只有单一 bit 的寄存器。当它被置位 1 后,就关掉了所有可屏蔽的异常(中断),只剩下 NMI 和 HardFault 可以响应。缺省值是 0,表示没有屏蔽中断;
PRIMASK 只是屏蔽掉中断,而并不是不让中断源产生中断!

2.3、BASEPRI
所有优先级号大于等于该值的中断都被关闭(优先级号越大,优先级越低);默认值是0,也就是不关闭任何中断;

2.4、FAULTMASK
这也是只有 1 bit 的寄存器,当设置为 1 的时候,只有 NMI 才能够响应,其他所有的异常,甚至是 HardFault 也不响应,默认值是 0,也就是都响应;

2.5、CONTROL
CONTROL[0]  用来指明运行的 CPU 的特权级别;
CONTROL[1] 用来指明使用的堆栈类型;
处理器在特权等级(Privilege )下,代码可以访问任意的寄存器;
也就是说,特权级和用户级的区别在于,访问 Core 寄存器的限制!

简言之,线程模式就是跑普通代码时候处理器所处的模式,Handler 模式就是异常的时候处理器的模式;

1、MSP:主堆栈指针;
2、PSP:用户堆栈指针;

CONTROL 寄存器么,它是 2 bit 构成,CONTROL[1] 用来决定使用哪个堆栈!
CONTROL[1] = 0 的时候,使用 MSP
CONTROL[1] = 1 的时候,使用 PSP

在简单的应用场景下,如果裸机的情况下,不打算对系统进行任何保护,CM3 上电后默认系统跑在特权的线程模式,默认使用 MSP 作为 SP 堆栈指针(即 CONTROL[1] = 0 );中断/异常 Handler 下也使用 MSP 作为 SP 堆栈指针;


当 CONTROL[1] = 1 的时候,线程模式不在使用 MSP,而是使用 PSP(Handler 模式永远使用 MSP);

那么什么时候使用 PSP 呢?比如你要跑一个 RTOS,多任务,那么每个任务都需要有自己的堆栈,此刻 PSP 就可以用起来了;PSP 将用户堆栈和系统堆栈 MSP 分开,防止用户堆栈破坏系统 OS 堆栈;

PSP将用户堆栈和系统堆栈MSP分开

在这种情况下的 PSP 与 MSP 切换,是硬件自动完成并压栈的,无需软件干预;

CM3 的中断/异常依赖于一个异常向量表,0~15 的编号为系统所用,大于 16 的编号为芯片公司自行定义的中断,最大支持到中断标号 255(一般用不到那么多);


这里主要分为几类:
1、Reset Handler:复位信号;
2、NMI:不可屏蔽信号,通过接 NMI 引脚;
3、系统各种 fault:包括 HardFault,BusFault,MemManageFault,UsageFault;
4、SVC 系统调用;
5、PendSV:给 OS 调度预留;
6、IRQ #xxx:芯片公司定义;


当系统发生中断/异常的时候,CM3 处理器会:
1、入栈:将 8 个寄存器的值压入栈;
2、取向量:从向量表中获取对应中断的 ISR 入口地址;
3、选择堆栈指针 MSP/PSP,更新到堆栈指针 SP 中,更新链接寄存器 LR,更新 PC;

入栈就是在进入中断/异常服务程序之前的现场保存,硬件自动将 xPSR、PC、LR、R12、R3、R2、R1、R0 压入堆栈:

如果当中断/异常发生时刻,正在使用 PSP,则压入 PSP;否则压入 MSP;
一旦进入 ISR,那就一直使用 MSP;


8.1、SVC

主要的目的是用来呼叫系统调用,进入操作系统内核;

如果用户态的软件要访问硬件,需要通过系统调用(在 Linux 上的 open、write,read,ioclt 这些)进入内核态;那么这个 SVC(SWI)就是呼叫系统调用的方式;

这种方式使得用户代码和具体硬件无关,硬件全部交给 OS;

SVC 只是作为一个封皮,通过系统调用,进入 SVC Handler 特权级的 Handler 模式;


8.2、PendSV

PendSV 可以像普通中断一样被 Pending(往 NVIC 的 PendSV 的 Pend 寄存器写 1),常用的场合是 OS 进行上下文切换;

1、任务 A 呼叫 SVC 请求任务切换;

2、OS 收到请求,准备切换上下文,手动 Pending 一个 PendSV;

3、CPU 退出 SVC 的 ISR 后,发现没有其他 IRQ 请求,便立即进入 PendSV 执行上下文切换;

4、正确的切换到任务 B;

5、此刻发生了一个中断,开始执行此中断的 ISR;

6、ISR 执行一半,SYSTICK 来了,抢占了该 IRQ;

7、OS 执行一些逻辑,并手动 Pending PendSV 准备上下文切换;

8、退出 SYSTICK 的 ISR 后,由于之前的 IRQ 优先级高于 PendSV,所以之前的 ISR 继续执行;

9、ISR 执行完毕退出,此刻没有优先级更高的 IRQ,那么执行 PendSV 进行上下文切换;

10、PendSV 执行完毕,顺利切到任务 A,同时进入线程模式;


    if (xTaskCreate(FactoryTask, FACTORY_TASK_NAME, FACTORY_TASK_STACKSIZE, NULL, FACTORY_TASK_PRI, NULL) == pdPASS)

在调度器初始化的时候,会创建一个 Idle 任务,这样可以确保至少有一个任务在运行;此任务优先级最低,为 0;
空闲任务用来在处理被删除的任务的内存,所有删除任务后,一定要确保空闲任务被运行,这样才能够内存回收;

FreeRTOS 的任务调度基于周期性的 Tick 心跳,调度器从 Ready 状态列表中选择下一个优先级最高的任务投入运行;被阻塞的任务可以通过 event 来临或者阻塞时间到,重新进入 Ready 状态;

注:协作式调度需要任务主动放弃 CPU,下一个才能够被调度;抢占式调度由系统决定调度;


以 Cortex-M3 为例,堆栈是向下生长的 ,所以会走进 portSTACK_GROWTH < 0 的这个分支

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值