FreeRTOS笔记(二)

创建任务与删除任务

使用动态分配内存的函数如下:

使用静态分配内存的函数如下:

删除任务:

2.任务句柄

任务句柄(Task Handle)是 FreeRTOS 中用于标识任务的一个重要概念。它是一个指向任务控制块(TCB,Task Control Block)的指针,任务控制块包含了与任务相关的所有信息,例如任务的堆栈、任务的状态、优先级等。

任务句柄在 FreeRTOS 中的作用是使得任务之间能够进行引用和操作。你可以通过任务句柄来控制任务,如暂停任务、删除任务、查询任务的状态等。

任务句柄的作用

1、创建任务时:当创建一个任务时,可以通过 xTaskCreate() 函数将任务句柄传递给调用者,来标识创建的任务。

2、操作任务时:任务句柄可以用于暂停、恢复、删除、查询任务等操作。

任务句柄的引入使得任务管理变得更加灵活和高效。它允许你

1、操作特定任务:通过任务句柄,你可以精确地操作某个任务,如暂停、恢复、删除等,而不需要依赖任务名称或其他标识符。

2、提供任务间的隔离:任务句柄使得任务之间可以通过句柄来进行管理,避免直接操作任务的内部实现,从而提高系统的模块化。

3.估测任务栈的大小

1.每层局部变量最多保存9个,假设有4层,则4*4*9=144字节

2.恢复现场需要16*4=64字节

3.变量的个数(局部变量、结构体等等)

4.为什么RTOS中使用指针函数

为什么使用函数指针进行任务调度?

1、灵活性:函数指针使得 RTOS 可以动态地选择执行哪个任务处理函数。比如,可以根据任务的优先级、调度策略、外部事件等因素来决定调用哪个任务函数,而不需要事先硬编码每个任务的执行顺序。

2、可扩展性:任务可以在运行时被添加或移除,使用函数指针后,任务的管理和调度就变得更加灵活。例如,你可以创建新的任务处理函数并通过函数指针将其插入调度队列中,而不需要修改调度器的核心代码。

3、任务回调:有些任务处理函数可能在特定条件下被触发(例如超时、外部中断等)。函数指针能够方便地管理这些任务的回调逻辑。、

定时器回调机制:

在 RTOS 中,定时器回调机制用于在特定时间后执行某些任务。例如,设置定时器来触发某个任务的执行,或定期执行某个任务(如周期性任务)。为了实现这一点,RTOS 可以使用函数指针来指定在定时器超时后调用的回调函数。

为什么使用函数指针进行定时器回调?

1、动态设置回调:定时器的回调函数通常是在任务创建或定时器设置时动态指定的。通过函数指针,RTOS 可以在运行时根据不同的定时器需求,动态地为不同的定时器设置不同的回调函数。

2、解耦合定时器和任务:使用函数指针后,定时器管理代码与具体任务代码之间就没有直接的依赖关系。定时器可以独立于任务执行,回调函数通过函数指针进行调用,这使得系统更加灵活和模块化。

3、节省内存:如果没有函数指针,定时器需要每个定时任务都嵌入一块特定的代码。如果使用函数指针,定时器就只需要保存指向任务函数的指针,节省了大量的内存空间。

5.任务状态

Ready:就绪状态,在就绪队列内,等等cpu通过任务调度转换为运行状态,也可被挂起

Running:运行状态,可调换至剩余三种状态

Suspended:挂起(暂停)状态,只能转化为就绪状态

Blocked:阻塞状态,能转化为就绪状态或挂起状态

6.任务暂停与恢复

任务暂停,将任务从任何状态挂起,即暂停,函数为:

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

任务恢复,将任务从挂起状态恢复为就绪状态,函数为:

void vTaskResume( TaskHandle_t xTaskToResume )

传入的参数均为句柄。

7.RTOS创建任务机制

RTOS在初始化FReeRTOS环境后,进行创建任务,任务创建成功后,会将当前任务放入就绪队列内,并将当前任务TCB地址的值赋给全局变量pxCurrentTCB(当前正在运行的任务的任务控制块(TCB),而该任务通常是优先级最高的就绪任务),继续创建任务,直到任务创建完成,即当前pxCurrentTCB指向的是最后一个任务。然后启用任务调度器,故最后创建的任务会先被执行。

8.RTOS如何进行任务管理与调度

相同优先级的任务轮流运行,最高优先级的任务先运行;高优先级的任务未执行完,低优先级的任务无法执行;一旦高优先级的任务就绪,马上就会运行。

正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理系统中,任何时间里只能有一个任务处于运行状态。

非运行状态的任务,它处于这3中状态之一:阻塞(Blocked)、暂停(Suspended)、就绪(Ready)。就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。事件分为两类:时间相关的事件、同步事件。所谓时间相关的事件,就是设置超时时间:在指定时间内阻塞,时间到了就进入就绪状态。使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。怎么"发送信息"?方法很多,有:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。这些方法用来发送同步信息,比如表示某个外设得到了数据。

FReeRTOS会创建一个就绪链表,其长度为优先级大小的最大值,如我的最大优先级为56,则链表的长度就为56,其存放的就是当前优先级就绪或运行的任务。

在任务开始,会初始化一个时钟频率为1000Hz的tick,即tick的值为1ms,每隔1ms会触发一个tick中断,它会执行三个任务,分别是cnt++,作为时钟基准;判断 xDelayedTaskList的任务是否可以恢复(有阻塞态到运行态);发起调度。

在任务调度开始时,会先创建一个优先级为0的空闲任务,然后调度器后从就绪链表内从高到低遍历整个链表,直到找到当前优先级存储不为空的情况,然后会让pxCurrentTCB指向下一个同等优先级的任务,执行该任务,若有多个任务,则会轮流进行。

在执行当前任务时,若有更高优先级的任务被创建,则pxCurrentTCB会立刻指向当前优先级最高的任务(抢占式优先级)。若该任务执行了例如vTaskDelay函数,则该任务会被判定为阻塞态,然后移动到阻塞队列里,任务调度器会执行下一优先级的任务,并且刚刚被打断的任务默认为已经执行过,会执行同一有优先级的下一任务,当高优先级的任务delay结束后,在tick中断内会被检测出,然后放入就绪队列内,再次遍历链表,执行任务。若该任务在阻塞态时被挂起,则tick不会再恢复该任务,除非再次恢复该函数。

9.一个任务想要退出必须自杀或者他杀

如果一个任务执行完,没有自杀或者他杀后,会进入到prvTaskExitError()函数,它会关闭所有中断,并且进入死循环,所以任务结束后必须要自杀vTaskDelete(NULL);或者他杀vTaskDelete(传入任务句柄);但自杀时没有人帮你收尸(自己TCB的相关信息等等),所以引入了空闲函数。

10.空闲函数

空闲任务(ldle任务)的作用之一:释放被删除的任务的内存。

除了上述目的之外,为什么必须要有空闲任务?一个良好的程序,它的任务都是事件驱动的:平时大部分时间处于阻塞状态。有可能我们自己创建的所有任务都无法执行,但是调度器必须能找到一个可以运行的任务:所以,我们要提供空闲任务。在使用vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务:

空闲任务优先级为0:它不能阻碍用户任务运行

空闲任务要么处于就绪态,要么处于运行态,永远不会阻塞

空闲任务的优先级为0,这意味着一旦某个用户的任务变为就绪态,那么空闲任务马上被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务"抢占"(pre-empt)了空闲任务,这是由调度器实现的。

要注意的是:如果使用vTaskDelete()来删除任务,那么你就要确保空闲任务有机会执行,否则就无法释放被删除任务的内存。

### RTOS中句柄的功能和用途 在RTOS环境中,句柄用于唯一标识特定资源或对象实例。通过句柄可以方便地管理和操作这些资源,而无需直接处理底层实现细节[^1]。 #### 句柄的主要功能 - **资源管理**:每个创建的任务、队列、信号量等都会分配唯一的句柄值。这使得应用程序可以通过句柄来访问对应的内核对象。 - **提高安全性**:由于句柄只提供有限的操作接口,因此减少了误用风险并增强了系统的稳定性[^2]。 - **简化编程模型**:开发者只需关注高层逻辑设计,不必关心具体的数据结构布局以及内存位置等问题。 #### 实际应用案例 当调用`xTaskCreate()`函数创建新任务时会返回该任务句柄: ```c TaskHandle_t xCreatedTask; // 定义一个变量存储即将被创建的任务句柄 // 创建一个新的任务... BaseType_t result = xTaskCreate( prvMyTask, /* Task function */ "my_task", /* String with name of task */ configMINIMAL_STACK_SIZE, /* Stack size in words */ NULL, /* Parameter passed into task */ tskIDLE_PRIORITY, /* Priority at which the task is created */ &xCreatedTask /* Used to pass out the handle reference */ ); if (result == pdPASS && xCreatedTask != NULL){ // 成功创建了任务,并获得了它的句柄 } ``` 在此之后就可以利用此句柄对该任务执行各种控制命令,比如删除它(`vTaskDelete(xCreatedTask)`),改变优先级(`vTaskPrioritySet()`)等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值