应用程序可以包含多个任务。如果运行应用程序的微控制器只有一个内核,那么在任意给定时间,实际上只会有一个任务被执行。

1、创建任务:xTaskCreate()API函数

    任务创建时,告诉内核为它分配多大的栈空间。这个值指定的栈空间可以保存多少个字(word),而不是多少个字节(byte)。比如说,如果是32位宽的栈空间,传入的值为100,则将会分配400字节的栈空间(100*4bytes)。

    注:没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计算出来虽然是可能的,但大多数用户会先简单地赋予一个自认为合理的值,然后利用 FreeRTOS 提供的特性来确证分配的空间既不欠缺也不浪费。第六章包括了一些信息,可以知道如何去查询任务使用了多少栈空间。

    句柄:将在API调用中对该创建出来的任务进行引用,比如改变任务优先级或者删除任务。也可以通过任务句柄来调度任务,例如在解码地方 xTaskNotifyGive(rtkTaskHandle);进行句柄操作,在rtk任务中使用notifyValue = ulTaskNotifyTake(pdTRUE,10000);

    优先级:FreeRTOSConfig.h中设定的编译时配置常量configMAX_PRIORITIES值,即是最多可拥有的优先级数目,值越大,内核花销的内存空间越多。优先级号表示任务的优先级低,优先级号0表示最低优先级。有效的优先级号范围从0到(configMAxxx-1)。

    阻塞状态:如果一个任务正在等待某个事件,则称这个任务处于阻塞态blocked。阻塞态是非运行态的一个子状态。任务可以进入阻塞态以等待以下两种不同类型的事件:

      1.定时事件------这类事件可以是延迟到期或者绝对时间到点。比如说某个任务可以进入阻塞以延迟10ms。

      2.同步事件------源于其它任务或中断的事件。比如说,某个任务可以进入阻塞状态以等待队列中有数据到来。同步事件囊括了所有板级范围内的事件类型。FreeRtos的队列、二值信号量、计数信号量、互斥信号量(recursive semaphore,递归信号量),本文一律称为互斥信号量,因为其主要用于实现互斥访问。都可以用来实现同步事件。

    挂起状态:suspended也是非运行态的子状态。处于挂起态的任务对调度器而言是不可见的。让一个任务进入挂起状态的位于方法就是调用 vTaskSuspend()API函数;把一个挂起的任务唤醒的唯一方法就是调用 vTaskResume()或vTaskResumeFromISR()函数。大多数应用程序中都不会用到挂起状态。

    就绪状态:如果任务处于非运行状态,但既没有阻塞也没有挂起,则这个任务处于就绪ready状态。处于就绪态的任务能够被运行,但只是准备运行,而当前尚未运行。

2、队列

    RTOS中所有的通信与同步机制都是基于队列实现的。

3、中断

    必须说明的是,只有以“FromISR”或者“FROM_ISR”结束的API函数或宏才可以在中断服务例程中。

    如果某个中断处理要求特别紧急,其延迟处理任务的优先级可以设为最高,以保证延迟处理任务随时都抢占系统中的其它任务。这样,延迟处理任务就成为其对应的ISR退出后第一个执行的任务,在时间上紧接着ISR执行,相当于所有的处理都在ISR中完成一样。

    rtos摘录

    对一个信号量进行带阻塞性质的“take”调用,意思是进入阻塞态以等待事件发生。当事件发生后,ISR对同一个信号量进行“give”操作,使得延迟处理任务解除阻塞,从而事件在延迟处理任务中得到相应的处理。

I:信号量

    vSemaphoreCreateBinary() API 函数,创建二值信号量

    xSemaphoreTake() API 函数,带走(Taking)”一个信号量意为”获取(Obtain)”或”接收(Receive)”信号量。只有当信号量有效的时候才可以被获取。

    xSemaphoreGive()API函数,
    xSemaphoreGiveFromISR() API 函数,专门用于中断服务例程中。

    ---------------------------------------------------------------------------------------

    计数信号量

    xSemaphoreCreateCounting() API 函数

4、资源管理

    互斥:访问一个被多任务共享,或是被任务与中断共享的资源时,需要采用“互斥”技术以保证数据在任何时候都保持一致性。这样做的目的时要确保任务从开始访问资源就具有排他性,直至这个资源又恢复到完整状态。FreeRTOS提供了多种特性用以实现互斥,但最好的互斥方法(如果可能得话任何时候都当如此)还是通过精心设计应哟个程序,尽量不要共享资源,或者时每个资源都通过单任务访问。

    1.基本临界区是指宏 taskENTER_CRITICAL()与 taskEXIT_CRITICAL()之间的代码区间。

    2.vTaskSuspendAll() API 函数,挂起调度器。挂起调度器可以停止上下文切换而不用关中断。如果某个中断在调度器挂起过程中要求进行上下文切换,则个这请求也会被挂起,直到调度器被唤醒后才会得到执行。

       xTaskResumeAll() API 函数,嵌套调用 vTaskSuspendAll()和 xTaskResumeAll()是安全的,因为内核有维护一个嵌套深度计数。调度器只会在嵌套深度计数为 0 时才会被唤醒——即在为每个之前调用的 vTaskSuspendAll()都配套调用了 xTaskResumAll()之后。

    3.互斥量

      在用于互斥的场合,互斥量从概念上可看作是与共享资源关联的令牌。一个任务想要合法地访问资源,其必须先成功地得到(Take)该资源对应的令牌(成为令牌持有者)。当令牌持有者完成资源使用,其必须马上归还(Give)令牌。只有归还了令牌,其它任务才可能成功持有,也才可能安全地访问该共享资源。一个任务除非持有了令牌,否则不允许访问共享资源。
    xSemaphoreCreateMutex() API 函数,
    xSemaphoreGive( );释放互斥信号量
    xSemaphoreTake( );获取互斥信号量

     互斥量是一种信号量。 FreeRTOS 中所有种类的信号量句柄都保存在类型为xSemaphoreHandle 的变量中。互 斥 量 在 使 用 前 必 须 先 创 建 。
rtos摘录

    优先级反转
图 38 也展现出了采用互斥量提供互斥功能的潜在缺陷之一。在这种可能的执行流程描述中,高优先级的任务 2 竟然必须等待低优先级的任务 1 放弃对互斥量的持有权。高优先级任务被低优先级任务阻塞推迟的行为被称为”优先级反转”。这是一种不合理的
行为方式,如果把这种行为再进一步放大,当高优先级任务正等待信号量的时候,一个介于两个任务优先之间的中等优先级任务开始执行——这就会导致一个高优先级任务在等待一个低优先级任务。

    优先级继承
FreeRTOS 中互斥量与二值信号量十分相似——唯一的区别就是互斥量自动提供了一个基本的”优先级继承”机制。优先级继承是最小化优先级反转负面影响的一种方案——其并不能修正优先级反转带来的问题,仅仅是减小优先级反转的影响。优先级继承
使得系统行为的数学分析更为复杂,所以如果可以避免的话,并不建议系统实现对优先级继承有所依赖。
优先级继承暂时地将互斥量持有者的优先级提升至所有等待此互斥量的任务所具有的最高优先级。持有互斥量的低优先级任务”继承”了等待互斥量的任务的优先级。这种机制在图 40 中进行展示。互斥量持有者在归还互斥量时,优先级会自动设置为其原来的优先级。
5、内存管理

6、问题排查

    1.打印,

      当调用标准 C 库函数时,栈空间使用量可能会急剧上升,特别是 IO 与字符串处理函数,比如 sprintf()。在 FreeRTOS 下载包中有一个名为 printf-stdarg.c 的文件。这个文件实现了一个栈效率优化版的小型 sprintf(),可以用来代替标准 C 库函数版本。在大多数情况下,这样做可以使得调用 sprintf()及相关函数的任务对栈空间的需求量小很多。

      printf-stdarg.c 源代码开放,但是为第三方所有。所以此源代码的 license 独立于FreeRTOS。具体的 license 条款包含在该源文件的起始部分。
 

    2.栈溢出,

      uxTaskGetStackHighWaterMark() API 函数,

      每个任务都独立维护自己的栈空间, 栈空间总量在任务创建时进行设定。
uxTaskGetStackHighWaterMark()主要用来查询指定任务的运行历史中, 其栈空间还差多少就要溢出。这个值被称为栈空间的”高水线(High Water Mark)”。
 

相关文章: