一 任务状态介绍:
实时操作系统(RTOS)中的任务通常具有多种状态,这些状态描述了任务在其生命周期中的不同阶段。以下是一些常见的任务状态及其解释,并附带一个简化的示例:
-
就绪态(Ready):
- 任务已经准备好执行,所有必要资源都已经准备就绪,只等待被RTOS调度器选中并分配CPU时间。 示例:任务A被创建后,被赋予了足够的堆栈空间和优先级,等待调度器调度。
-
运行态(Running):
- 任务正在执行,占据了CPU资源。 示例:任务A被调度器选中,其上下文被加载到CPU寄存器中,开始执行任务代码。
-
阻塞态(Blocked):
- 任务暂时不能执行,通常是因为等待某些外部事件,如I/O操作完成、定时器到期、信号量或互斥量被释放等。 示例:任务A调用了
vTaskDelay()
函数等待一定时间间隔,这时任务A进入了阻塞态,直到延时期满后才会变为就绪态。
- 任务暂时不能执行,通常是因为等待某些外部事件,如I/O操作完成、定时器到期、信号量或互斥量被释放等。 示例:任务A调用了
-
挂起态(Suspended):
- 任务被人为挂起,不会参与调度,除非通过API函数恢复其就绪状态。 示例:任务A通过API函数
vTaskSuspend()
被手动挂起,不再参与调度,直到通过vTaskResume()
恢复。
- 任务被人为挂起,不会参与调度,除非通过API函数恢复其就绪状态。 示例:任务A通过API函数
-
终止态(Terminated):
- 任务执行完毕或被删除,生命周期结束,不再参与调度。 示例:任务A完成了所有预定工作并通过
vTaskDelete()
函数被删除,这时任务A处于终止态,其占用的系统资源将被回收。
- 任务执行完毕或被删除,生命周期结束,不再参与调度。 示例:任务A完成了所有预定工作并通过
下面是一个简化的示例代码片段,展示了任务在不同状态之间的切换:
C
1#include <freertos/FreeRTOS.h>
2#include <freertos/task.h>
3
4void vTaskFunction(void *pvParameters)
5{
6 while (1)
7 {
8 // 任务开始时,处于就绪态
9 printf("Task running...\n");
10
11 // 进入阻塞态,等待1秒钟
12 vTaskDelay(pdMS_TO_TICKS(1000));
13
14 // 延时过后,任务再次变为就绪态
15 printf("Task resumed after delay...\n");
16
17 // 假设某个条件满足,任务被挂起
18 if (someCondition)
19 vTaskSuspend(NULL);
20
21 // 当条件改变,任务被恢复
22 if (conditionChanged)
23 vTaskResume(NULL);
24
25 // 如果任务要结束,可以删除自身
26 if (shouldTerminate)
27 vTaskDelete(NULL);
28 }
29}
30
31int main()
32{
33 // 创建任务并初始化,此时任务处于就绪态
34 xTaskCreate(vTaskFunction, "MyTask", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
35
36 // 启动RTOS调度器
37 vTaskStartScheduler();
38
39 // 实际永远不会达到此处,因为调度器无限循环
40 return 0;
41}
在上述示例中,任务在vTaskDelay()
函数调用后进入阻塞态,当延时期满后恢复到就绪态。如果满足特定条件,任务还会经历挂起和恢复的过程,最后根据需要还可删除自身。调度器会根据任务状态和优先级做出调度决策,确保实时性和任务间的协调执行。
二 任务阻塞态实例
在实时操作系统(RTOS)中,任务在等待I/O操作完成、定时器到期、信号量或互斥量被释放等情况下的实际例子如下:1. **等待I/O操作完成**:
#include <freertos/FreeRTOS.h>
#include <driver/gpio.h>
void vTaskWaitForIO(void *pvParameters)
{
gpio_config_t ioConfig;
ioConfig.pin_bit_mask = GPIO_SEL_4; // 假设配置GPIO4引脚
ioConfig.mode = GPIO_MODE_INPUT;
gpio_config(&ioConfig);
while (1)
{
// 任务等待I/O操作完成(例如,等待按键按下)
while (gpio_get_level(GPIO_NUM_4) == 0)
{
// I/O操作尚未完成(按键未按下),任务进入阻塞态
vTaskDelay(pdMS_TO_TICKS(10)); // 微小延时,防止CPU过度占用
}
// I/O操作完成(按键被按下),任务继续执行
printf("Button pressed!\n");
}
}
int main()
{
xTaskCreate(vTaskWaitForIO, "IO_Wait_Task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
vTaskStartScheduler();
return 0;
}
上述示例中,任务一直在等待GPIO引脚电平变化,当电平变化(例如,按键按下)时,任务从阻塞态恢复到就绪态。2. **等待定时器到期**:
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
TimerHandle_t xTimer;
void vTaskWaitForTimer(void *pvParameters)
{
// 创建一个周期性定时器,周期为1秒
xTimer = xTimerCreate("Periodic_Timer", pdMS_TO_TICKS(1000), pdTRUE, NULL, NULL);
if (xTimer != NULL)
{
// 启动定时器
xTimerStart(xTimer, portMAX_DELAY);
while (1)
{
// 任务等待定时器到期
if (xTimerIsTimerActive(xTimer))
{
// 定时器还未到期,任务进入阻塞态
vTaskDelay(pdMS_TO_TICKS(10));
}
else
{
// 定时器到期,任务继续执行
printf("Timer expired!\n");
// 重新启动定时器
xTimerStart(xTimer, portMAX_DELAY);
}
}
}
}
int main()
{
xTaskCreate(vTaskWaitForTimer, "Timer_Wait_Task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
vTaskStartScheduler();
return 0;
}
在这个例子中,任务一直在等待定时器到期,当定时器到期时,任务从阻塞态恢复到就绪态。3. **等待信号量或互斥量被释放**:
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
SemaphoreHandle_t xSemaphore;
void vTaskWaitForSemaphore(void *pvParameters)
{
xSemaphore = xSemaphoreCreateBinary();
if (xSemaphore != NULL)
{
// 其他任务或中断可能释放信号量
// 假设一开始信号量未释放,任务等待信号量
while (xSemaphoreTake(xSemaphore, portMAX_DELAY) != pdTRUE)
{
// 信号量尚未释放,任务进入阻塞态
}
// 信号量被释放,任务继续执行
printf("Semaphore taken!\n");
// 进行相应处理后释放信号量,以便其他任务可以获取
xSemaphoreGive(xSemaphore);
}
}
void vOtherTaskReleasesSemaphore(void *pvParameters)
{
// 其他任务在适当的时候释放信号量
vTaskDelay(pdMS_TO_TICKS(5000)); // 延迟5秒后释放
xSemaphoreGive(xSemaphore);
}
int main()
{
xTaskCreate(vTaskWaitForSemaphore, "Semaphore_Wait_Task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
xTaskCreate(vOtherTaskReleasesSemaphore, "Semaphore_Release_Task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
vTaskStartScheduler();
return 0;
}
在这个例子中,`vTaskWaitForSemaphore`任务在等待信号量被释放,当`vOtherTaskReleasesSemaphore`任务释放信号量时,`vTaskWaitForSemaphore`任务从阻塞态恢复到就绪态。同样的逻辑也适用于互斥量(Mutexes)。