【FreeRtos内部机制-韦东山(更新中)】

【FreeRtos内部机制-韦东山(更新中)】_第1张图片

1 任务的引入_ARM框架

任务:**一段代码;运行位置;运行环境----------------->即运行起来的函数**

补充ARM架构,以F103为例;数据保存在内存,代码保存在Flash
【FreeRtos内部机制-韦东山(更新中)】_第2张图片

**  内存四区**

> 堆区(heap):一般由程序员手动分配释放(动态内存申请与释放),若程序员不释放,程序结束时可能由操作系统回收。
> 栈区(stack):由编译器自动分配释放,**存放函数的形参、局部变量**等。当函数执行完毕时自动释放。 
> 全局区(global /stack):用于存放全局变量和静态变量, 里面细分有一个常量区,一些常量存放在此。该区域是在程序结束后由操作系统释放。 
> 代码区(code/ text):用于存放程序代码,字符串常量也存放于此。 ————————————————

【FreeRtos内部机制-韦东山(更新中)】_第3张图片

> 内存四区原文链接:https://blog.csdn.net/weixin_44966641/article/details/120456141

1.1 探究 代码执行流程

a=a+b;

【FreeRtos内部机制-韦东山(更新中)】_第4张图片

1.1.1 读变量,读到哪里去—寄存器

【FreeRtos内部机制-韦东山(更新中)】_第5张图片

**CPU运行时,先去Flash上取得指令,再执行指令:

* 把内存a的值读入CPU寄存器R0
* 把内存b的值读入CPU寄存器R1
* 把R0、R1累加,存入R0
* 把R0的值写入内存a

** CPU内部有R0、R1、……、R15共16个寄存器
  * R13,别名SP,栈寄存器,保存着栈的地址
  * R14,别名LR,返回地址,保存着函数的返回地址
  * R15,别名PC,程序计数器,也就是当期程序运行到哪了

【FreeRtos内部机制-韦东山(更新中)】_第6张图片

1.1.2 常用汇编指令补充

【FreeRtos内部机制-韦东山(更新中)】_第7张图片

* 读内存:Load,LDR
* 写内存:Store,STR
* 加法:ADD
* 入栈:PUSH,实质上就是写内存STR
* 出栈:POP,实质上就是读内存LDR
* LDR R0, [R1, #0x00]
  * 源地址:R1+0x00,注意:不是读R1,是把R1的值当做内存的地址
  * 目的:R0,CPU的寄存器
  * 长度:4字节,LDR指令就是读4字节,LDRH是读2字节,LDRB是读1字节



要写内存:写内存哪个地址?从哪里得到数据?写多少字节?

* STR R0, [R1, #0x00]
  * 目的地址:R1+0x00,注意:不是写R1,是把R1的值当做内存的地址
  * 源:R0,CPU的寄存器
  * 长度:4字节,STR指令就是读4字节,STRH是读2字节,STRB是读1字节



入栈:把CPU的寄存器的值,写到内存上

* PUSH {R3, LR}

  * 源:CPU的寄存器R3、LR的值
  * 目的:内存,内存哪里?使用CPU的SP寄存器指定内存地址
  * 长度:大括号里所有寄存器的数据长度,每个寄存器4字节
  * 注意:低编号的寄存器,保存在内存的低地址处

出栈:把内存中的数值,写到CPU的寄存器

* POP {R3, PC}
  * 源:内存,内存哪里?使用CPU的SP寄存器指定内存地址
  * 目的:CPU的寄存器R3、PC的值
  * 长度:大括号里所有寄存器的数据长度,每个寄存器4字节
  * 注意:内存的低地址处的数据,写到CPU低编号的寄存器

1.2 栈的作用

1.2.1 代码运行汇编分析

【FreeRtos内部机制-韦东山(更新中)】_第8张图片
【FreeRtos内部机制-韦东山(更新中)】_第9张图片
【FreeRtos内部机制-韦东山(更新中)】_第10张图片

1.2.2 栈与现场

> ① 什么是现场?
> 
> 			暂且认为现场就是:当前被打断瞬间所有寄存器的值
> 
> ②  怎么保存现场?  		
> 		保存现场 :保存在内存里 ③ 内存在哪里?  	 16个寄存器保存在栈里
> 
>      任务:函数+保存现场(栈)==运行中的函数

③ 保存现场的几种场景

任务切换:
			保存所有寄存器的值	
	函数调用:
			保存某些寄存器
			传参--不用保存
	中断处理:
				硬件:保存一部分----栈
				软件处理中断:保存一些用到的寄存器

2 创建函数任务

2.1 创建任务的函数简析

【FreeRtos内部机制-韦东山(更新中)】_第11张图片

xTaskCreate函数原型:

【FreeRtos内部机制-韦东山(更新中)】_第12张图片

> xTaskCreate函数: 
> 					pxTaskCode:函数
> 					pcName: 任务名
> 					usStackDepth:栈大小,malloc 分配
> 					pvParameters:参数
> 					uxPriority:优先级
> 					pxCreatedTask:TCB结构体

2.2 创建任务的内部细节

2.2.1 TCB结构体

参数详解:
* 分配了TCB结构体
* 分配了栈
* 在栈里写入了函数地址、参数




```c
> 启用vTask函数:**往PC里放入函数地址,R0寄存器放入参数**

结构体成员
【FreeRtos内部机制-韦东山(更新中)】_第13张图片

栈的大小分配

> 从哪里分配?
>1个巨大数组划分一部分内存用作栈,如下图,起始地址保存在TCB的pxSTACK里。 			
> 分配多大?
> 局部变量和调用深度

【FreeRtos内部机制-韦东山(更新中)】_第14张图片
【FreeRtos内部机制-韦东山(更新中)】_第15张图片

2.2 任务的调度机制与任务切换

2.2.1 优先级与状态

1 优先级不同

>   高优先级的任务,优先执行,可以抢占低优先级的任务 
>   高优先级的任务不停止,低优先级的任务永远无法执行 
>   同等优先级的任务,轮流执行:时间片轮转

2 状态

>    运行态:running   
>    就绪态:ready  
>    阻塞:blocked,等待某件事(时间、事件)   
>    暂停:suspend,休息去了

2.2.2 任务调度

3 怎么取出要运行的任务

>  找到最高优先级的运行态、就绪态任务,运行它
>  
>  如果大家平级,轮流执行:排队,链表前面的先运行,运行1个tick后乖乖地去链表尾部排队
>  
>  

【FreeRtos内部机制-韦东山(更新中)】_第16张图片

4 谁进行调度?

>  * TICK中断-----即定时器中断
  

【FreeRtos内部机制-韦东山(更新中)】_第17张图片

2.2.3 任务状态的切换

【FreeRtos内部机制-韦东山(更新中)】_第18张图片不同的链表来维护不同状态的任务。

高优先级的任务3完成后从就绪ready链表进入delay链表,----->任务12得以执行
空闲任务:IdleTask: 清理工作
五个tick 后再次进入ready链表,继续统治

2.3 任务调度深入探讨

通过链表深入理解调度机制

* 可抢占:高优先级的任务先运行

* 时间片轮转:同优先级的任务轮流执行

* 空闲任务礼让:如果有同是优先级0的其他就绪任务,空闲任务主动放弃一次运行机会

【FreeRtos内部机制-韦东山(更新中)】_第19张图片

3 消息队列(queue)

【FreeRtos内部机制-韦东山(更新中)】_第20张图片

3.1 多任务系统中的互斥引入

【FreeRtos内部机制-韦东山(更新中)】_第21张图片
【FreeRtos内部机制-韦东山(更新中)】_第22张图片

定义全局变量a,多任务系统:A,B函数,均进行a++;--------------a=1
如何引入互斥机制,保证数据的可控性?
使用队列,用已经写好的API函数,关中断---写数据----开中断。

3.2 队列 好处

【FreeRtos内部机制-韦东山(更新中)】_第23张图片

【FreeRtos内部机制-韦东山(更新中)】_第24张图片

3.3 环形缓冲区

【FreeRtos内部机制-韦东山(更新中)】_第25张图片

队列核心是:关中断、环形缓冲区、链表

3.4 队列结构体

【FreeRtos内部机制-韦东山(更新中)】_第26张图片
【FreeRtos内部机制-韦东山(更新中)】_第27张图片
在这里插入图片描述

3.4.1 队列读流程

【FreeRtos内部机制-韦东山(更新中)】_第28张图片

3.4.2 队列写流程

【FreeRtos内部机制-韦东山(更新中)】_第29张图片

4 信号量和互斥量

4.1 信号量操作流程

信号量核心: 计数值; 
int count; list;

 **1 获取信号量: Take 操作**
	 a 关中断
	 b if (count>0){
	 	count--;
	 	return ok;
	 }
	 c else {1return Error;//不等待2)休眠:
	 			a):放入SemaphoreList
	 			b):ReadList[]--->DelayList
 	
 	d 被唤醒:
 		count--;
	 	return ok;

 	 **2 释放信号量: Give 操作** 
 		 a 关中断
 		 b count++;
 		 c SemaphoreList非空?有任务等待? wake up它。

在这里插入图片描述

4.2 互斥量

【FreeRtos内部机制-韦东山(更新中)】_第30张图片
【FreeRtos内部机制-韦东山(更新中)】_第31张图片

互斥量就是特殊的队列。

互斥量更是特殊的信号量,

互斥量实现了优先级继承。

低优先级任务获得互斥量,此时高优先级任务索取互斥量----->失败休眠。
HPTask提升低优先级的等级(优先级继承),使其快速搞定释放互斥量,然后恢复自己卑劣的地位,并唤醒HPTask;

【FreeRtos内部机制-韦东山(更新中)】_第32张图片

5 事件组

【FreeRtos内部机制-韦东山(更新中)】_第33张图片

1 创建
2 等待(哪些位? 与/或,timeout)
	a 关调度器
	b 当前事件变量uxEventBits是否满足需要
		满足;
		不满足:返回Err
				休眠:放入 event_group; readlist---->delayedList
				
	
3 设置事件
	a 设置事件:	uxEventBits
	b 唤醒xTaskWatingForBits队列上“所有”满足条件的任务			
	
事件组为什么不关中断?
事件中的中断不会设置“值”,只是触发/唤醒“守护任务”

6 任务通知的内部机制

### 8.1 核心: 通知状态、通知值

#### 8.1.1 通知状态

一个任务的"通知状态"有三种:

- taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
- taskWAITING_NOTIFICATION:任务在等待通知
- taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为 pending(有数据了,待处理)  



一个任务想等待别人发来通知,可以调用`ulTaskNotifyTake  `或`xTaskNotifyWait  `:

- 可能别人早就发来通知:"通知状态"为taskNOTIFICATION_RECEIVED,那么函数立刻返回
- 可能别人还没发来通知:这些函数把"通知状态"从taskNOT_WAITING_NOTIFICATION
- 改为taskWAITING_NOTIFICATION,然后休眠



别的任务可以使用`xTaskNotifyGive`或`xTaskNotify `给某个任务发通知:

- 会马上唤醒对方
- 无条件唤醒对方,不管对方期待什么数据

【FreeRtos内部机制-韦东山(更新中)】_第34张图片
【FreeRtos内部机制-韦东山(更新中)】_第35张图片
【FreeRtos内部机制-韦东山(更新中)】_第36张图片

你可能感兴趣的:(单片机,stm32)