提示:此内容涉及部分汇编+数据结构+计算机操作系统
目录
FreeRTOS调度机制
优先级与状态
- 优先级不同
- 高优先级的任务,优先执行,可以抢占低优先级的任务
- 高优先级的任务不停止,低优先级的任务永远无法执行
- 同等优先级的任务,轮流执行:时间片轮转
- 状态
不同的状态通过对应的链表来管理
- 运行态:running
- 就绪态:ready
- 阻塞:blocked,等待某件事(时间、事件)
- 暂停:suspend,休息去了
- 怎么管理?
- 怎么取出要运行的任务?
- 找到最高优先级的运行态、就绪态任务,运行它
- 如果大家平级,轮流执行:排队,链表前面的先运行,运行1个tick后乖乖地去链表尾部排队
- 怎么取出要运行的任务?
调度方法:TICK中断
如果优先级为0 则需要注意空闲任务
FreeRTOS调度链表
相关函数
一、xTaskCreate 创建任务相关
作用为:
- 分配TCB结构体
- 分配栈区 在栈区
- 写入的函数地址和参数
xTaskCreate:函数原型:
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode,//任务函数
const char * const pcName,//任务名字
const configSTACK_DEPTH_TYPE usStackDepth,//栈大小 (用来保护寄存器的值)
void * const pvParameters,//传递给任务函数的参数(就是传给第一行的任务函数)
UBaseType_t uxPriority,//任务优先级
TaskHandle_t * const pxCreatedTask//TCB结构体 (传出的函数)
//TCB 任务控制块
)
任务控制块结构体 tskTaskControlBlock。
在FreeRTOS中,任务的执行是由系统调度的。系统为每个任务都定义了一个任务控制块,这个任务控制块里面存有任务的所有信息,如任务的栈指针,任务名称,任务的形参等。系统对任务的全部操作都可以通过这个任务控制块来实现。任务控制块结构体:
为什么任务控制块中没有看到 :函数指针以及传入给函数的参数 因为栈中 R15:PC会保存函数指针 R0:会保存函数参数 以便下次调用时使用
函数原型:tskTaskControlBlock
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
//pxTopOfStack指向栈顶 此栈顶的值由 是由PC寄存器 赋给RO寄存器 函数就会开始执行
//PC寄存器保存的就是函数地址
ListItem_t xStateListItem; //<任务的状态列表项被引用的列表表示该任务的状态(Ready, Blocked, Suspended)。* /
ListItem_t xEventListItem; //用于从事件列表中引用任务。
UBaseType_t uxPriority; //任务的优先级。0为最低优先级
StackType_t *pxStack; /*指向堆栈的开始。* /
//注意此处的指针在freeRtos中 是指向一个全局数组 此数组的内存用来分配给栈
} tskTCB;
三、 QueueDefinition(队列结构体)
typedef struct QueueDefinition
{
int8_t *pcHead;
/*指向队列数据存储的起始位置*/
int8_t *pcTail;
/*指向队列存储区域末尾的字节。一旦分配了多余的字节来存储队列项,这是用来做记号的。 */
int8_t *pcWriteTo;
/*< 指向存储区域中空闲的下一个位置。 */
union/* 使用union是编码标准的一个例外,以确保两个互斥结构成员不会同时出现(浪费RAM)。*/
{
int8_t *pcReadFrom;
/*指向结构用作队列时读取队列项的最后一个位置。*/
UBaseType_t uxRecursiveCallCount;
/*在结构用作互斥锁时,维护递归互斥锁被递归地“占用”的次数的计数。*/
} u;
List_t xTasksWaitingToSend;
/*阻塞的任务列表,这些任务等待发送到此队列。按优先顺序存储。*/
List_t xTasksWaitingToReceive;
/*队列等待读取的任务列表,这些任务等待接收此队列的数据。按优先顺序存储。 */
volatile UBaseType_t uxMessagesWaiting;
/*当前队列中的列表项数目。 */
UBaseType_t uxLength;
/*队列的长度定义为它将持有的链表项的数量,而不是字节的数量。*/
UBaseType_t uxItemSize;
/*队列持有的每个列表项的大小 */
volatile int8_t cRxLock;
/*存储在锁定队列时从队列接收(从队列中删除)的项数。当队列未锁定时,设置为queue解锁。*/
volatile int8_t cTxLock;
/*< 存储传输到队列的项数(添加到队列中)当队列被锁定时。当队列未锁定时,设置为queue解锁。*/
} xQUEUE;
四、 EventGroup_t(事件组结构体)
struct EventGroupDef_t;
typedef struct EventGroupDef_t * EventGroupHandle_t; //控制块句柄(指针)
typedef struct EventGroupDef_t //事件标志组结构体(控制块)
{
EventBits_t uxEventBits; // 当前事件 每一位用来表示一个任务
List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
//等待被设置的任务列表
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxEventGroupNumber;
#endif
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
#endif
} EventGroup_t;
进程通信方式:
- 信号量
- 消息队列
- 管道
匿名(需要血缘关系)
有名(不需要血缘关系)
高级(另一个程序在当前程序打开,算是当前程序的子进程,称高级管道) - socket(套接字)
- 共享内存通信(最快)
共享内存怎么做?
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
- 创建共享内存,指定key、size、mode(权限)
- 将当前进程连接到共享内存
- 把共享内存的地址读取出来,并强制转换为本地变量
- 读写操作了
做法:将同一段物理内存映射到堆与栈之间的那一段虚拟内存中,