2.2 任务管理

概述

TencentOS tiny内核是单地址空间的可抢占式实时内核,TencentOS tiny内核不提供进程模型,任务对应线程的概念,是最小的调度运行体,也是最小的资源持有单位。

任务的本质是一个拥有独立栈空间的可调度运行实体,用户可以在任务的入口函数中编写自己的业务逻辑;多个任务之间可以通过系统提供的任务间通信机制进行同步或者信息传递等操作;每个任务都有优先级,高优先级任务可以抢占低优先级任务的运行。

API讲解

创建任务的系统api接口为tos_task_create,接口原型如下:

  1. k_err_t tos_task_create(k_task_t *task,
  2. char *name,
  3. k_task_entry_t entry,
  4. void *arg,
  5. k_prio_t prio,
  6. k_stack_t *stk_base,
  7. size_t stk_size,
  8. k_timeslice_t timeslice);

这里详细讲解此api参数意义:

  • task

这是一个k_task_t类型的指针,k_task_t是内核的任务结构体类型。注意:task指针,应该指向生命周期大于待创建任务体生命周期的k_task_t类型变量,如果该指针指向的变量生命周期比待创建的任务体生命周期短,譬如可能是一个生命周期极端的函数栈上变量,可能会出现任务体还在运行而k_task_t变量已被销毁,会导致系统调度出现不可预知问题。

  • name

指向任务名字符串的指针。注意:同task,该指针指向的字符串生命周期应该大于待创建的任务体生命周期,一般来说,传入字符串常量指针即可。

  • entry

任务体运行的函数入口。当任务创建完毕进入运行状态后,entry是任务执行的入口,用户可以在此函数中编写业务逻辑。

  • arg

传递给任务入口函数的参数。

  • prio

任务优先级。prio的数值越小,优先级越高。用户可以在tos_config.h中,通过TOS_CFG_TASK_PRIO_MAX来配置任务优先级的最大数值,在内核的实现中,idle任务的优先级会被分配为TOS_CFG_TASK_PRIO_MAX - 1,此优先级只能被idle任务使用。因此对于一个用户创建的任务来说,合理的优先级范围应该为[0, TOS_CFG_TASK_PRIO_MAX - 2]。另外TOS_CFG_TASK_PRIO_MAX的配置值必需大于等于8。

  • stk_base

任务在运行时使用的栈空间的起始地址。注意:同task,该指针指向的内存空间的生命周期应该大于待创建的任务体生命周期。stk_base是k_stack_t类型的数组起始地址。

  • stk_size

任务的栈空间大小。注意:因为stk_base是k_stack_t类型的数组指针,因此实际栈空间所占内存大小为stk_size * sizeof(k_stack_t)。

  • timeslice

时间片轮转机制下当前任务的时间片大小。当timeslice为0时,任务调度时间片会被设置为默认大小(TOS_CFG_CPU_TICK_PER_SECOND / 10),系统时钟滴答(systick)数 / 10。

编程实例

1、在tos_config.h中,配置最大任务优先级TOS_CFG_TASK_PRIO_MAX:

#define TOS_CFG_TASK_PRIO_MAX 10u

2、配置每秒钟的系统滴答数TOS_CFG_CPU_TICK_PER_SECOND:

#define TOS_CFG_CPU_TICK_PER_SECOND 1000u

3、编写main.c示例代码:

  1. #include "tos.h" // 添加TencentOS tiny内核接口头文件
  2. #include "mcu_init.h" // 包含mcu初始化头文件,里面有board_init等板级启动代码函数原型声明
  3.  
  4. #define STK_SIZE_TASK_PRIO4 512 // 优先级为4的任务栈大小为512
  5. #define STK_SIZE_TASK_PRIO5 1024 // 优先级为5的任务栈大小为1024
  6.  
  7. k_stack_t stack_task_prio4[STK_SIZE_TASK_PRIO4]; // 优先级为4的任务栈空间
  8. k_stack_t stack_task_prio5[STK_SIZE_TASK_PRIO5]; // 优先级为5的任务栈空间
  9.  
  10. k_task_t task_prio4; // 优先级为4的任务体
  11. k_task_t task_prio5; // 优先级为5的任务体
  12.  
  13. extern void entry_task_prio4(void *arg); // 优先级为4的任务体入口函数
  14. extern void entry_task_prio5(void *arg); // 优先级为5的任务体入口函数
  15.  
  16. uint32_t arg_task_prio4_array[3] = { // 优先级为4的任务体入口函数入参
  17. 1, 2, 3,
  18. };
  19.  
  20. char *arg_task_prio5_string = "arg for task_prio5"; // 优先级为5的任务体入口函数入参
  21.  
  22. static void dump_uint32_array(uint32_t *array, size_t len)
  23. {
  24. size_t i = 0;
  25.  
  26. for (i = 0; i < len; ++i) {
  27. printf("%d\t", array[i]);
  28. }
  29. printf("\n\n");
  30.  
  31. }
  32.  
  33. void entry_task_prio4(void *arg)
  34. {
  35. uint32_t *array_from_main = (uint32_t *)arg; // 捕获调用者传入的参数
  36.  
  37. printf("array from main:\n");
  38. dump_uint32_array(array_from_main, 3); // dump传入的参数(数组)
  39.  
  40. while (K_TRUE) {
  41. printf("task_prio4 body\n"); // 任务运行体,不断打印这条信息
  42. tos_task_delay(1000); // 睡眠1000个系统时钟滴答(以下记作systick),因为TOS_CFG_CPU_TICK_PER_SECOND为1000,也就是一秒钟会有1000个systick,因此睡眠1000个systick就是睡眠了1秒。
  43. }
  44. }
  45.  
  46. void entry_task_prio5(void *arg)
  47. {
  48. int i = 0;
  49. char *string_from_main = (char *)arg;
  50. printf("string from main:\n");
  51. printf("%s\n\n", string_from_main); // 打印出调用者传入的字符串参数
  52.  
  53. while (K_TRUE) {
  54. if (i == 2) {
  55. printf("i = %d\n", i); // i为2时,挂起task_prio4,task_prio4停止运行
  56. tos_task_suspend(&task_prio4);
  57. } else if (i == 4) {
  58. printf("i = %d\n", i); // i为4时,恢复task_prio4的运行
  59. tos_task_resume(&task_prio4);
  60. } else if (i == 6) {
  61. printf("i = %d\n", i); // i为6时,删除task_prio4,task_prio4不再运行
  62. tos_task_destroy(&task_prio4);
  63. }
  64. printf("task_prio5 body\n");
  65. tos_task_delay(1000);
  66. ++i;
  67. }
  68. }
  69.  
  70. int main(void)
  71. {
  72. board_init(); // 执行板级初始化代码,初始化串口等外设。
  73. tos_knl_init(); // 初始化TencentOS tiny内核
  74. // 创建一个优先级为4的任务
  75. (void)tos_task_create(&task_prio4, "task_prio4", entry_task_prio4,
  76. (void *)(&arg_task_prio4_array[0]), 4,
  77. stack_task_prio4, STK_SIZE_TASK_PRIO4, 0);
  78. // 创建一个优先级为5的任务
  79. (void)tos_task_create(&task_prio5, "task_prio5", entry_task_prio5,
  80. (void *)arg_task_prio5_string, 5,
  81. stack_task_prio5, STK_SIZE_TASK_PRIO5, 0);
  82. // 开始内核调度
  83. tos_knl_start();
  84. }

运行效果

array from main:1 2 3

task_prio4 bodystring from main:arg for task_prio5

task_prio5 bodytask_prio4 bodytask_prio5 bodytask_prio4 bodyi = 2task_prio5 bodytask_prio5 bodyi = 4task_prio4 bodytask_prio5 bodytask_prio4 bodytask_prio5 bodytask_prio4 bodyi = 6task_prio5 bodytask_prio5 bodytask_prio5 bodytask_prio5 bodytask_prio5 bodytask_prio5 bodytask_prio5 body

实例代码