先分配内存空间
初始化消息队列
系统自动为控制块方培内存空间
如存储位置,头指针,为指针,消息大小,队列长度,
消息队列和消息空间在同一段内存空间中,创建水分配了消息空间呵消息队列的容量,无法更改。
发送一般消息:
若队列未满或者允许覆盖,将信息拷贝到队尾。否则,根据指定的超时时间阻塞。
若等到队列未满,或者超过了阻塞时间,则从阻塞状态转换为就绪状态,后者会收到错误码。
发送紧急消息,消息会被复制到消息队列队头。
读取消息队列
如果队列为空,任务保持阻塞状态。队列不空时,自动转变为就绪态。超过了阻塞时间自动转变为就绪态。
在中断总发消息不允许带有阻塞机制
中断必须快速响应和硬件中断
任务调度器不会在中断中运行
高优先级中断可能要更快地执行
如果在中断过程中更高优先级的任务被唤醒,中断返回后会返回更高优先级的任务。
消息队列创建:创建一个消息队列返回这个队列的队列句柄。
每创建一个新的队列需要对其分配RAM,存储状态和队列信息。
xQueueCreate():
使用动态内存分配,需将configSUPPORT_DYNAMIC_ALLOCATION 置1,(FreeRTOSConfig.h)
xQueueCreateStatic() :静态内存分配
prvInitialiseNewQueue()
参数:消息队列长度,单个消息大小,存储消息起始地址
消息队列类型
未分配存储消息的内存空间: 单个消息大小为0时将pchead指向队列控制块 pxNewQueue,因为消息队列用作互斥量时pchead设置为NULL。
分配了存储消息的内存空间: pcHead 指向存储消息的
起始地址 pucQueueStorage。
初始化消息队列控制块->重置消息队列
重置消息队列 xQueueGenericReset()
进入临界段是为了确保多任务并发环境中的数据一致性
需要恢复发送阻塞消息队列,便于数据发送
队列创建
队列创建时也需要进入临界区:临界区锁定的中断屏蔽
内存分配和指针初始化不能被打断,被错误的初始化或访问。导致内存访问错误或者队列崩溃。
通用消息队列发送函数 xQueueGenericSend()
队列未满-》添加信息
恢复等待消息阻塞的任务
- 若恢复的任务优先级比当前任务高-》切换任务
- 如果没有等待的任务,拷贝成功也需要任务切换
队列已满-》
不指定阻塞超时时间,退出,返回错误
初始化阻塞超时结构体变量,初始化进入
结束之后
检查是否超时
如果队列还是满的-》添加任务到等待发送列表中和
接受信息xQueueGenericReceive()
从队列中接收消息并把消息从队列中删除
接收的信息以拷贝的形式存在,必须提供一个足够大的缓存区(中断时必须使用特定的函数)
设置阻塞时间,若队列为空,任务保持阻塞状态。若等待时间超过两万指定的阻塞时间,直接从阻塞状态转化成就绪态。
挂起任务调度器和进入临界区
在将任务设置为阻塞的过程中,挂起调度器并进入临界区
挂起调度器意味着任务不能切换并且不准调用可能引起任务切换的 API 函数。 但挂起调度器并不会禁止中断,中断服务函数
仍然可以操作队列事件列表,可能会解除任务阻塞、可能会进行上下文切换,这也是不允许的。解决办法是还要给队列上锁,禁止任何中断来操作队列
假设有一个高优先级任务 A 和一个低优先级任务 B。任务 A 想入队,但队列已满,任务 A 将进入阻塞状态,并等待队列空闲。如果在任务 A 被正确地阻塞并添加到等待列表之前,低优先级任务 B 或者中断修改了队列的状态,它可能抢先完成入队操作
互斥量
递归获取信号量时可能出现死锁的情况:
- 信号量用完:
- 阻塞 vs 死锁:
单次获取信号量且信号量不可用,任务会进入阻塞状态,此时系统能够恢复正常运行。然而,如果任务递归获取信号量,并且没有释放信号量,导致信号量完全用尽,那么任务自身将无法继续执行,且也无法释放之前获取的信号量,这就形成了死锁。此时,没有其他任务可以释放信号量,系统无法恢复,导致死锁。
使用临界区资源的方法:
获取并释放信号量
xSemaphoreTake()xSemaphoreGive()
挂起任务调度器 vTaskSuspendAll();xTaskResumeAll();
禁用中断
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()