一、时间管理
- 时间片轮转机制
概述
TencentOS tiny操作系统内核是一个抢占式内核,抢占式内核的特点是,如果最高优先级的任务不放弃CPU(调用tos_task_delay、tos_task_yeild等主动放权,或者任务间同步通信机制的pend接口等),那么CPU将会一直被此任务独占。
假设这样一种场景:系统中包含多个同等优先级的任务,且这几个任务体中都没有放弃CPU的行为,则会出现的情况是,这几个任务始终只有第一个被得到调度的那个在运行,因为第一个得到调度的任务体中不会主动放弃CPU,而其他任务优先级上与其相等无法抢占。此种场景下,其他任务会因得不到CPU而陷入饥饿状态。
时间片轮转机制提供了按时间片占用调度的策略,可以解决上述场景下的任务饥饿问题。
API讲解
编程实例
1、在tos_config.h中,配置时间片轮转组件开关TOS_CFG_ROUND_ROBIN_EN:
#define TOS_CFG_ROUND_ROBIN_EN 1u
2、编写main.c示例代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "cmsis_os.h"
#include "stdio.h"
#include "tos_k.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define STK_SIZE_TASK_DEMO 512
#define STK_SIZE_TASK_SAMPLE 512
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/*
此代码中创建了两个同等优先级(PRIO_TASK_DEMO)的任务task_demo1、task_demo2,两个任务体中做的操作是不断对各自的计数器(demo1_counter、demo2_counter)做自增操作(没有放弃CPU的操作),时间片分别为timeslice_demo1、timeslice_demo2。
同时创建了一个优先级比task_demo1、task_demo2较高的采样任务task_sample,此任务不间歇的对两个计数器进行采样。在开启时间片轮转的情况下,task_demo1、task_demo2得到运行的时间比例应该是timeslice_demo1与timeslice_demo2的比例,那么demo1_counter和demo2_counter值的比例应该也差不多是timeslice_demo1与timeslice_demo2的比例。
*/
// task_demo1和task_demo2的优先级
#define PRIO_TASK_DEMO 4
// 采样任务的优先级
#define PRIO_TASK_SAMPLE (PRIO_TASK_DEMO - 1)
// task_demo1的时间片,在tos_task_create时传入
const k_timeslice_t timeslice_demo1 = 10;
// task_demo2的时间片,在tos_task_create时传入
const k_timeslice_t timeslice_demo2 = 20;
k_stack_t stack_task_demo1[STK_SIZE_TASK_DEMO];
k_stack_t stack_task_demo2[STK_SIZE_TASK_DEMO];
k_stack_t stack_task_sample[STK_SIZE_TASK_SAMPLE];
k_task_t task_demo1;
k_task_t task_demo2;
k_task_t task_sample;
extern void entry_task_demo1(void *arg);
extern void entry_task_demo2(void *arg);
extern void entry_task_sample(void *arg);
uint64_t demo1_counter = 0;
uint64_t demo2_counter = 0;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
void entry_task_demo1(void *arg)
{
while (K_TRUE) {
++demo1_counter;
}
}
void entry_task_demo2(void *arg)
{
while (K_TRUE) {
++demo2_counter;
}
}
void entry_task_sample(void *arg)
{
while (K_TRUE) {
++demo2_counter;
printf("demo1_counter: %lld\n", demo1_counter);
printf("demo2_counter: %lld\n", demo2_counter);
printf("demo2_counter / demo1_counter = %f\n",
(double)demo2_counter / demo1_counter);
printf("should almost equals to:\n");
printf("timeslice_demo2 / timeslice_demo1 = %f\n\n", (double)timeslice_demo2 / timeslice_demo1);
tos_task_delay(1000);
}
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
tos_knl_init();
// 配置robin机制参数
tos_robin_default_timeslice_config((k_timeslice_t)500u);
(void)tos_task_create(&task_demo1, "demo1", entry_task_demo1, NULL,
PRIO_TASK_DEMO, stack_task_demo1, STK_SIZE_TASK_DEMO,
timeslice_demo1);
(void)tos_task_create(&task_demo2, "demo2", entry_task_demo2, NULL,
PRIO_TASK_DEMO, stack_task_demo2, STK_SIZE_TASK_DEMO,
timeslice_demo2);
(void)tos_task_create(&task_sample, "sample", entry_task_sample, NULL,
PRIO_TASK_SAMPLE, stack_task_sample, STK_SIZE_TASK_SAMPLE,
0);
tos_knl_start();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3、运行效果
源码链接