RT-Thread学习笔记【线程管理】

客官°小女子只卖身不卖艺 2023-01-03 06:15 141阅读 0赞

线程管理

划分线程的思想:将一个大任务分解成多个小任务

线程是实现任务的载体,是RTT的最基本调度单位,描述了一个任务执行的运行环境和优先等级

线程在运行时会认为自己是以独占CPU的方式运行

上下文:线程执行时的运行环境,即包括所有寄存器变量、堆栈、内存信息在内的各个变量和数据

功能特点

主要功能:对线程进行管理和调度

将所有线程分为系统线程用户线程

系统线程:由RTT内核创建的线程

用户线程:由应用程序创建的线程

线程从内核对象容器中分配线程对象,当线程被删除时也会被从对象容器中删除

RTT线程调度器为抢占式:从就绪线程列表中查找最高优先级线程,保证它能够被运行,最高优先级任务一旦就绪,能得到CPU的使用权;当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的CPU使用权就会被让出给更高优先级的线程;当中断服务程序让一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行

调度器切换线程的步骤:

  1. 保存当前线程上下文/挂起被中断线程
  2. 切换到高优先级线程
  3. 等待高优先级线程运行完毕
  4. 切换回之前的线程
  5. 恢复当前线程上下文

工作机制

  1. 线程控制块结构体struct rt_thread

线程控制块:操作系统用于管理线程的数据结构,存放了线程的优先级、名称、状态等信息和线程之间连接用的链表结构和西安测绘给你等待事件集合等

  1. struct rt_thread
  2. {
  3. /* 内核线程对象 */
  4. char name[RT_NAME_MAX];//线程名
  5. rt_uint8_t type;//对象类型
  6. rt_uint8_t flags;//线程标志位
  7. #ifdef RT_USING_MODULE
  8. void *module_id;//应用组件的id
  9. #endif
  10. rt_list_t list;//对象列表
  11. rt_list_t tlist;//线程列表
  12. /* 栈顶指针和入口函数 */
  13. void *sp;//栈顶指针sp
  14. void *entry;//入口函数指针
  15. void *parameter;//参数
  16. void *stack_addr;//栈地址指针
  17. rt_uint32_t stack_size;//栈大小
  18. /* 错误代码 */
  19. rt_err_t error;//线程错误代码
  20. rt_uint8_t stat;//线程状态
  21. /* 线程优先级 */
  22. rt_uint8_t current_priority;//当前优先级
  23. rt_uint8_t init_priority;//初始优先级:线程创建时指定的线程优先级,一般在运行中不会改变
  24. #if RT_THREAD_PRIORITY_MAX > 32
  25. rt_uint8_t number;//线程代号
  26. rt_uint8_t high_mask;
  27. #endif
  28. rt_uint32_t number_mask;//线程代号掩码
  29. #if defined(RT_USING_EVENT)
  30. /* 线程事件 */
  31. rt_uint32_t event_set;
  32. rt_uint8_t event_info;
  33. #endif
  34. #if defined(RT_USING_SIGNALS)
  35. rt_sigset_t sig_pending;//等待信号量
  36. rt_sigset_t sig_mask;//信号位掩码
  37. void *sig_ret;//从信号量返回栈顶的指针
  38. rt_sighandler_t *sig_vectors;//信号量句柄向量
  39. void *si_list;//信号量列表
  40. #endif
  41. rt_ubase_t init_tick;//线程初始化计数值
  42. rt_ubase_t remaining_tick;//线程剩余计数值
  43. struct rt_timer thread_timer;//内置线程计数器
  44. void (*cleanup)(struct rt_thread *tid)//线程退出清除函数
  45. /* light weight process if present */
  46. #ifdef RT_USING_LWP
  47. void *lwp;
  48. #endif
  49. rt_uint32_t user_data;//用户数据
  50. };
  51. typedef struct rt_thread *rt_thread_t;//以struct rt_thread* 的形式封装

cleanup()函数会在线程退出时被空闲线程回调一次,执行用户设置的清理现场等工作

user_data可由用户挂接一些数据信息到线程控制块中来提供类似线程私有数据的实现

  1. 线程栈

RTT为线程开辟独立的栈,当进行线程切换时,将当前线程的上下文存在栈中,当线程恢复运行时再从栈中读取上下文恢复现场;线程栈还存放了函数中的局部变量:函数局部变量初始时从寄存器中分配,当这个函数再调用另一个函数时,这些局部变量会被放入线程栈中

RTT3.1.0以前的版本只支持栈自顶向下生长(从高地址向低地址增长)

栈大小设置:可以利用FinSH中的list_thread命令查看线程运行过程中使用的栈大小,多次调整余量来确定

  1. 线程状态

同一时间只允许一个线程在CPU中运行,RTT将线程划分为5种状态










































状态 描述 运行特征 宏定义
初始态 线程刚创建,还没开始运行的状态 线程不参与调度 RT_THREAD_INIT
就绪态 按优先级排队,等待被执行的状态 当前线程运行完毕让出CPU时,最高优先级的就绪态程序会被操作系统指示运行 RT_THREAD_READY
运行态 正在运行的线程的状态 单核系统中只有rt_thread_self()函数返回的线程处于运行状态;多和系统中可能有多个线程处于运行状态 RT_THREAD_RUNNING
阻塞态 因为挂起而无法参与调度的线程状态 因为资源不可用或线程主动延时被挂起的线程会进入此状态 RT_THREAD_SUSPEND
关闭态 线程运行结束时的状态 不参与线程调度 RT_THREAD_CLOSE
  1. 线程优先级

优先级:线程被调度的优先程度

县城越重要,优先级应越高,线程被调度的可能性越大

RTT最多支持0-255共256个线程优先级,数值越小优先级越高,0为最高优先级

资源比较紧张的系统中可选择只支持8个活32个优先级的系统配置

ARN Cortex-M系列CPU普遍采用32个优先级,最低优先级默认分配给空闲线程使用,用户一般不使用

线程调度器优先满足高优先级线程的抢占行为

  1. 时间片

每个线程都有时间片这个参数,但时间片仅对优先级相同就绪态线程有效

系统对有效线程采用时间片轮转的方式进行调度,时间片约束了线程单次运行的时长,其单位是1个系统节拍(OS Tick)

时间片设置为n,则该线程在和其他线程切换执行时就执行n个节拍的时长

详见后面的“线程同步”部分

  1. 线程入口函数

线程控制块中的

  1. void *entry;//入口函数指针

是入口函数,可以设置为无限循环模式实现顺序执行或有限次循环模式实现

无限循环模式实现:

  1. /* 示例:线程1的入口函数 */
  2. static void thread1_entry(void *parameter)
  3. {
  4. rt_uint32_t count = 0;
  5. while (1)
  6. {
  7. /* 线程1采用低优先级运行,一直打印计数值 */
  8. rt_kprintf("thread1 count: %d\n", count++);
  9. rt_thread_mdelay(500);
  10. }
  11. }
  12. /* 原型 */
  13. void thread_entry(void* paramenter)
  14. {
  15. while(1)
  16. {
  17. /*等待事件发发生并对事件进行服务、处理*/
  18. }
  19. }

注意线程中不能陷入死循环操作,必须有让出CPU使用权的动作(如调用延时函数或主动挂起)

一般而言,无限循环的线程用于处理需要一直运行的程序,不会被系统自动删除

顺序执行或有限次循环模式实现:

  1. /* 示例:线程2入口 */
  2. static void thread2_entry(void *param)
  3. {
  4. rt_uint32_t count = 0;
  5. /* 线程2拥有较高的优先级,可抢占线程1而获得执行 */
  6. for (count = 0; count < 10 ; count++)
  7. {
  8. /* 线程2打印计数值 */
  9. rt_kprintf("thread2 count: %d\n", count);
  10. }
  11. rt_kprintf("thread2 exit\n");
  12. /* 线程2运行结束后将自动被系统删除 (线程控制块和线程栈依然在idle线程中释放) */
  13. }
  14. /* 原型 */
  15. static void thread_entry(void* parameter)
  16. {
  17. /*顺次处理事务*/
  18. }

这个模式下的线程会在执行完毕后被系统自动删除

  1. 线程错误码

每个线程都有一个用于保存错误码的变量

错误码如下:

  1. /* RTT错误码定义 */
  2. #define RT_EOK 0 /**< 无错误 */
  3. #define RT_ERROR 1 /**< 普通错误 */
  4. #define RT_ETIMEOUT 2 /**< 超时错误 */
  5. #define RT_EFULL 3 /**< 资源满 #define RT_EEMPTY 4 /**< 资源空 */
  6. #define RT_ENOMEM 5 /**< 内存不足 */
  7. #define RT_ENOSYS 6 /**< 系统不支持 */
  8. #define RT_EBUSY 7 /**< 系统忙 */
  9. #define RT_EIO 8 /**< IO错误 */
  10. #define RT_EINTR 9 /**< 中断系统调用 */
  11. #define RT_EINVAL 10 /**< 非法参数 */
  1. 线程状态切换

线程状态切换图如下

在这里插入图片描述

创建线程:调用函数rt_thread_create/init()

线程就绪:调用函数rt_thread_startup()

线程运行:线程调度器将就绪态线程的状态变为运行态

线程阻塞:由上图①中的函数将运行态线程挂起为阻塞态

返回就绪:处于阻塞态的线程如果等待超时仍未能获得资源或由于其他线程释放了自身资源,它将返回就绪态

线程主动关闭:阻塞态线程调用函数rt_thread_delete/detach()进入关闭态

线程被动关闭:运行态线程在运行结束时由系统自动调用函数rt_thread_exit()转变为关闭态

注意:RTT中实际上线程并不存在运行态,就绪态和运行态是等价的,他们之间的区别是就绪态的线程不被线程调度器允许使用CPU资源,即 运行态线程就是有CPU可用的就绪态线程

  1. 系统线程

RTT内核中的系统线程分为空闲线程和主线程

空闲线程:系统创建的最低优先级线程,线程状态永远为就绪态

主线程:系统启动时创建的main线程

当系统中无其他就绪态线程存在时,调度器将调度空闲线程,它通常是一个永远不能被挂起的死循环

空闲线程就是系统执行“空闲”这个操作的线程,当系统“闲下来”的时候就让空闲线程顶上干活

若某线程运行完毕,系统将自动执行rt_thread_exit()函数:

  1. void rt_thread_exit(void)
  2. {
  3. struct rt_thread *thread;
  4. register rt_base_t level;
  5. /* 获得运行完毕线程的信息 */
  6. thread = rt_current_thread;
  7. /* 关闭中断 */
  8. level = rt_hw_interrupt_disable();
  9. /* 从系统就绪队列中删除线程 */
  10. rt_schedule_remove_thread(thread);
  11. /* 将线程状态更改为关闭态 */
  12. thread->stat = RT_THREAD_CLOSE;
  13. /* 从计时器列表中脱离线程计时器 */
  14. rt_timer_detach(&thread->thread_timer);
  15. if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) && thread->cleanup == RT_NULL)
  16. {
  17. rt_object_detach((rt_object_t)thread);//如果该线程是系统线程,则从线程队列中脱离
  18. }
  19. else//否则
  20. {
  21. /* 挂入僵尸队列 */
  22. rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
  23. }
  24. /* 使能中断 */
  25. rt_hw_interrupt_enable(level);
  26. /* 切换到下一个任务 */
  27. rt_schedule();
  28. }

僵尸队列:资源未回收但处于关闭状态的线程队列

僵尸队列就是死了但还没人给他收尸的线程组成的队列

空闲线程的任务就是给被删除的线程收尸——回收被删除线程的资源

空闲线程也提供了接口来运行用户设置的钩子函数,可以钩入功耗管理、看门狗喂狗等工作


系统线程的入口函数是main_thread_entry(),用户的应用入口函数main()从这里真正开始

系统调度器启动后,main线程开始运行,用户可以在main()函数中添加自己的应用程序初始化代码

具体启用流程参见“系统启动”部分

线程管理方式

线程的基本操作概述

创建/初始化线程、启动线程、运行线程、删除/脱离线程

对于动态线程使用rt_thread_create()创建,动态线程会自动从动态内存堆上分配栈空间与线程句柄(隐含条件:初始化heap后才能使用create创建动态线程);使用rt_thread_init()初始化静态线程,由用户为其分配栈空间和线程句柄

创建和删除线程

使用一下接口创建一个动态线程:

  1. rt_thread_t rt_thread_create(const char *name,//线程名称,最大长度由宏RT_NAME_MAX指定,多余部分自动裁掉
  2. void (*entry)(void *parameter),//线程入口函数
  3. void *parameter,//入口函数参数
  4. rt_uint32_t stack_size,//线程栈大小
  5. rt_uint8_t priority,//线程优先级,数字越小优先级越高
  6. rt_uint32_t tick)//线程时间片大小,指定线程一次调度能运行的最大时间长度,时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
  7. {
  8. struct rt_thread *thread;
  9. void *stack_start;
  10. thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,name);//分配内核对象
  11. if (thread == RT_NULL)//检测是否分配成功
  12. return RT_NULL;
  13. stack_start = (void *)RT_KERNEL_MALLOC(stack_size);//分配线程句柄 并 按参数指定的栈大小开出动态堆内存空间
  14. if (stack_start == RT_NULL)
  15. {
  16. /* 如果分配栈失败 */
  17. rt_object_delete((rt_object_t)thread);//删除当前线程
  18. return RT_NULL;
  19. }
  20. _rt_thread_init(thread,
  21. name,
  22. entry,
  23. parameter,
  24. stack_start,
  25. stack_size,
  26. priority,
  27. tick);//初始化线程
  28. return thread;
  29. }

注意分配出的栈空间是按照rtconfig.h中配置的RT_ALIGN_SIZE方式对齐

线程创建成功返回线程句柄,失败则返回RT_NULL

使用rt_thread_delete()删除线程

  1. rt_err_t rt_thread_delete(rt_thread_t thread)
  2. {
  3. rt_base_t lock;
  4. /* 检查线程是否合法 */
  5. RT_ASSERT(thread != RT_NULL);
  6. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  7. RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread) == RT_FALSE);
  8. if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT)
  9. {
  10. /* 从任务列表中移除线程 */
  11. rt_schedule_remove_thread(thread);
  12. }
  13. /* 释放线程定时器 */
  14. rt_timer_detach(&(thread->thread_timer));
  15. /* 改变线程状态为关闭态 */
  16. thread->stat = RT_THREAD_CLOSE;
  17. /* 关闭中断 */
  18. lock = rt_hw_interrupt_disable();
  19. /* 将线程放入僵尸线程队列 */
  20. rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
  21. /* 使能中断 */
  22. rt_hw_interrupt_enable(lock);
  23. return RT_EOK;
  24. }

这个函数仅在使能了系统动态堆时才有效(即已经定义RT_USING_HEAP)

用此函数删除线程接口仅仅释放掉了部分控制资源,让线程变为关闭态,不让它再使用CPU,真正释放线程控制块和释放线程栈要到下次执行空闲线程时,由空闲线程执行最后的线程删除任务

也就是说rt_thread_delete()负责把线程弄死,但尸体还堆在内存里,空闲线程会完成收尸工作

初始化和脱离线程

使用rt_thread_init()接口完成静态线程对象初始化

  1. rt_err_t rt_thread_init(struct rt_thread *thread,//线程句柄
  2. const char *name,//线程名称
  3. void (*entry)(void *parameter),//入口函数
  4. void *parameter,//入口函数参数
  5. void *stack_start,//线程栈起始地址
  6. rt_uint32_t stack_size,//线程栈大小
  7. rt_uint8_t priority,//线程优先级
  8. rt_uint32_t tick)//线程时间片大小
  9. {
  10. /* 检查线程是否合法 */
  11. RT_ASSERT(thread != RT_NULL);
  12. RT_ASSERT(stack_start != RT_NULL);
  13. /* 初始化线程内核对象 */
  14. rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);
  15. /* 进行线程底层初始化 */
  16. return _rt_thread_init(thread,
  17. name,
  18. entry,
  19. parameter,
  20. stack_start,
  21. stack_size,
  22. priority,
  23. tick);
  24. }
  25. //进行线程底层初始化函数
  26. static rt_err_t _rt_thread_init(struct rt_thread *thread,//线程句柄
  27. const char *name,//线程名称
  28. void (*entry)(void *parameter),//入口函数
  29. void *parameter,//入口函数参数
  30. void *stack_start,//线程栈起始地址
  31. rt_uint32_t stack_size,//线程栈大小
  32. rt_uint8_t priority,//线程优先级
  33. rt_uint32_t tick)//线程时间片大小
  34. {
  35. /* 初始化线程列表 */
  36. rt_list_init(&(thread->tlist));
  37. thread->entry = (void *)entry;//传参
  38. thread->parameter = parameter;
  39. /* 初始化栈 */
  40. thread->stack_addr = stack_start;
  41. thread->stack_size = stack_size;
  42. /* 初始化线程栈 */
  43. rt_memset(thread->stack_addr, '#', thread->stack_size);//设置物理内存
  44. thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
  45. (void *)((char *)thread->stack_addr + thread->stack_size - 4),
  46. (void *)rt_thread_exit);
  47. //栈顶指针初始化:设置栈顶指针指向、传入参数、进行物理内存栈顶地址系统对齐(ARM使用4字节对齐)、分配线程退出程序
  48. /* 线程优先级设置 */
  49. RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
  50. thread->init_priority = priority;
  51. thread->current_priority = priority;
  52. thread->number_mask = 0;
  53. #if RT_THREAD_PRIORITY_MAX > 32
  54. thread->number = 0;
  55. thread->high_mask = 0;
  56. #endif
  57. /* 时间片初始化 */
  58. thread->init_tick = tick;
  59. thread->remaining_tick = tick;
  60. /* 错误和运行状态初始化 */
  61. thread->error = RT_EOK;//正常运行
  62. thread->stat = RT_THREAD_INIT;//初始态
  63. /* 初始化cleanup和用户数据 */
  64. thread->cleanup = 0;
  65. thread->user_data = 0;
  66. /* 初始化线程定时器 */
  67. rt_timer_init(&(thread->thread_timer),
  68. thread->name,
  69. rt_thread_timeout,
  70. thread,
  71. 0,
  72. RT_TIMER_FLAG_ONE_SHOT);
  73. /* 初始化信号量 */
  74. #ifdef RT_USING_SIGNALS
  75. thread->sig_mask = 0x00;
  76. thread->sig_pending = 0x00;
  77. thread->sig_ret = RT_NULL;
  78. thread->sig_vectors = RT_NULL;
  79. thread->si_list = RT_NULL;
  80. #endif
  81. #ifdef RT_USING_LWP
  82. thread->lwp = RT_NULL;
  83. #endif
  84. RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
  85. return RT_EOK;
  86. }

线程创建成功返回RT_EOK;失败返回RT_ERROR

对于使用rt_thread_init()初始化的静态线程,使用rt_thread_detach()将线程对象脱离线程队列和内核对象管理器

  1. rt_err_t rt_thread_detach(rt_thread_t thread)
  2. {
  3. rt_base_t lock;
  4. /* 检查线程是否合法 */
  5. RT_ASSERT(thread != RT_NULL);
  6. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  7. RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread));
  8. if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT)
  9. {
  10. /* 从计划任务中去除 */
  11. rt_schedule_remove_thread(thread);
  12. }
  13. /* 释放线程定时器 */
  14. rt_timer_detach(&(thread->thread_timer));
  15. /* 改变线程状态为关闭态 */
  16. thread->stat = RT_THREAD_CLOSE;
  17. /* 脱离线程 */
  18. rt_object_detach((rt_object_t)thread);
  19. if (thread->cleanup != RT_NULL)
  20. {
  21. /* 关闭中断 */
  22. lock = rt_hw_interrupt_disable();
  23. /* 将线程放入僵尸线程队列 */
  24. rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
  25. /* 使能中断 */
  26. rt_hw_interrupt_enable(lock);
  27. }
  28. return RT_EOK;
  29. }

线程创建成功返回RT_EOK;失败返回RT_ERROR

启动线程

使用rt_thread_startup()将处于初始态的线程启动为就绪态

  1. rt_err_t rt_thread_startup(rt_thread_t thread)
  2. {
  3. /* 检查线程是否合法 */
  4. RT_ASSERT(thread != RT_NULL);
  5. RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
  6. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  7. /* 设置当前优先级和初始优先级一致 */
  8. thread->current_priority = thread->init_priority;
  9. /* 计算优先级值 */
  10. #if RT_THREAD_PRIORITY_MAX > 32
  11. thread->number = thread->current_priority >> 3; /* 5bit */
  12. thread->number_mask = 1L << thread->number;
  13. thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
  14. #else
  15. thread->number_mask = 1L << thread->current_priority;
  16. #endif
  17. RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
  18. thread->name, thread->init_priority));
  19. /* 改变线程状态为阻塞态 */
  20. thread->stat = RT_THREAD_SUSPEND;
  21. /* 恢复线程为就绪态 */
  22. rt_thread_resume(thread);
  23. if (rt_thread_self() != RT_NULL)
  24. {
  25. /* 将线程加入任务序列 */
  26. rt_schedule();
  27. }
  28. return RT_EOK;
  29. }
  30. //恢复线程函数
  31. rt_err_t rt_thread_resume(rt_thread_t thread)
  32. {
  33. register rt_base_t temp;
  34. /* 检查线程是否合法 */
  35. RT_ASSERT(thread != RT_NULL);
  36. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  37. RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: %s\n", thread->name));//debug用
  38. if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND)
  39. {
  40. RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
  41. thread->stat));
  42. return -RT_ERROR;
  43. }
  44. /* 关闭中断 */
  45. temp = rt_hw_interrupt_disable();
  46. /* 从阻塞列表中移除 */
  47. rt_list_remove(&(thread->tlist));
  48. rt_timer_stop(&thread->thread_timer);//暂停定时器
  49. /* 使能中断 */
  50. rt_hw_interrupt_enable(temp);
  51. /* 将线程加入任务序列 */
  52. rt_schedule_insert_thread(thread);
  53. RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));//调用线程恢复钩子函数
  54. return RT_EOK;
  55. }

获得当前线程

  1. rt_thread_t rt_thread_self(void)
  2. {
  3. return rt_current_thread;
  4. }

使线程让出CPU资源

使用此接口后,线程仍会在就绪队列中,但它不再是运行态,不在占有处理器

  1. rt_err_t rt_thread_yield(void)
  2. {
  3. register rt_base_t level;
  4. struct rt_thread *thread;
  5. /* 关闭中断 */
  6. level = rt_hw_interrupt_disable();
  7. /* 获取当前线程 */
  8. thread = rt_current_thread;
  9. /* 如果当前线程状态为就绪态 且 位于就绪队列中 */
  10. if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY && thread->tlist.next != thread->tlist.prev)
  11. {
  12. /* 将线程从队列中移除 */
  13. rt_list_remove(&(thread->tlist));
  14. /* 将线程放到就序队列末尾 */
  15. rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),&(thread->tlist));
  16. rt_hw_interrupt_enable(level);//激活调度器进行上下文切换
  17. rt_schedule();//继续执行任务
  18. return RT_EOK;
  19. }
  20. /* 使能中断 */
  21. rt_hw_interrupt_enable(level);
  22. return RT_EOK;
  23. }

yield与schedule的区别执行rt_thread_yield()后,当前线程被换出,相同优先级的下一个就绪线程被执行;执行rt_schedule()后,当前线程并不一定被换出,即使被换出也不会被放到就绪线程链表尾部,而是在系统中选取就绪的最高优先级线程执行,如果系统中没有更高优先级的线程存在,则系统将继续执行当前线程。即 yield让当前线程去队尾重新排队,schedule按正常手续从队里挑高优先级的插队干活

线程延时

  1. rt_err_t rt_thread_sleep(rt_tick_t tick)//延时tick个OS Tick
  2. {
  3. register rt_base_t temp;
  4. struct rt_thread *thread;
  5. /* 关闭中断 */
  6. temp = rt_hw_interrupt_disable();
  7. /* 获取当前线程 */
  8. thread = rt_current_thread;
  9. RT_ASSERT(thread != RT_NULL);
  10. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  11. /* 挂起线程(阻塞) */
  12. rt_thread_suspend(thread);
  13. /* 重新设定线程定时器的溢出时间并重启定时器 */
  14. rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
  15. rt_timer_start(&(thread->thread_timer));
  16. /* 使能中断 */
  17. rt_hw_interrupt_enable(temp);
  18. rt_schedule();
  19. /* 清除当前线程的超时错误标志 */
  20. if (thread->error == -RT_ETIMEOUT)
  21. thread->error = RT_EOK;
  22. return RT_EOK;
  23. }
  24. rt_err_t rt_thread_delay(rt_tick_t tick)//延时tick个OS Tick
  25. {
  26. return rt_thread_sleep(tick);//sleep函数套皮
  27. }
  28. rt_err_t rt_thread_mdelay(rt_int32_t ms)//延时ms个1ms
  29. {
  30. rt_tick_t tick;
  31. tick = rt_tick_from_millisecond(ms);//计算延迟时间
  32. return rt_thread_sleep(tick);//调用延迟函数
  33. }

挂起(阻塞)和恢复线程

线程主动挂起、资源不可用等情况下,线程将进入阻塞态,处于阻塞态的线程,如果其等待的资源超时,那么该线程将不再等待这些资源,并返回就绪状态;或当其他线程释放掉该线程所等待的资源时,该线程也会返回就绪态

  1. rt_err_t rt_thread_suspend(rt_thread_t thread)
  2. {
  3. register rt_base_t temp;
  4. /* 检查线程是否合法 */
  5. RT_ASSERT(thread != RT_NULL);
  6. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  7. RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name));
  8. if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY)
  9. {
  10. RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",
  11. thread->stat));
  12. return -RT_ERROR;
  13. }
  14. /* 停止中断 */
  15. temp = rt_hw_interrupt_disable();
  16. /* 改变线程状态为阻塞态 */
  17. thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);
  18. rt_schedule_remove_thread(thread);//从当前任务序列中将该线程移除
  19. /* 无条件停止定时器 */
  20. rt_timer_stop(&(thread->thread_timer));
  21. /* 使能中断 */
  22. rt_hw_interrupt_enable(temp);
  23. RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));//调用线程挂起钩子函数
  24. return RT_EOK;
  25. }

注意:通常不应使用这个函数来挂起线程,如果非要用,一定记得立刻调用rt_schedual()进行手动的线程上下文切换

恢复线程内容见上文

控制线程

使用以下接口可用于对线程进行特殊控制

  1. rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)
  2. {
  3. register rt_base_t temp;
  4. /* 检查线程是否合法 */
  5. RT_ASSERT(thread != RT_NULL);
  6. RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
  7. switch (cmd)//使用命令控制
  8. {
  9. case RT_THREAD_CTRL_CHANGE_PRIORITY://动态更改线程优先级
  10. /* 停止中断 */
  11. temp = rt_hw_interrupt_disable();
  12. if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY)//就绪态的线程
  13. {
  14. /* 将其从任务序列中移除 */
  15. rt_schedule_remove_thread(thread);
  16. /* 更改优先级 */
  17. thread->current_priority = *(rt_uint8_t *)arg;
  18. /* 重新计算优先级属性 */
  19. #if RT_THREAD_PRIORITY_MAX > 32
  20. thread->number = thread->current_priority >> 3; /* 5bit */
  21. thread->number_mask = 1 << thread->number;
  22. thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */
  23. #else
  24. thread->number_mask = 1 << thread->current_priority;
  25. #endif
  26. /* 将线程重新加入任务序列 */
  27. rt_schedule_insert_thread(thread);
  28. }
  29. else//运行态的线程
  30. {
  31. thread->current_priority = *(rt_uint8_t *)arg;
  32. /* 重新计算优先级 */
  33. #if RT_THREAD_PRIORITY_MAX > 32
  34. thread->number = thread->current_priority >> 3; /* 5bit */
  35. thread->number_mask = 1 << thread->number;
  36. thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */
  37. #else
  38. thread->number_mask = 1 << thread->current_priority;
  39. #endif
  40. }
  41. /* 使能中断 */
  42. rt_hw_interrupt_enable(temp);
  43. break;
  44. case RT_THREAD_CTRL_STARTUP://开始运行一个线程
  45. return rt_thread_startup(thread);
  46. #ifdef RT_USING_HEAP
  47. case RT_THREAD_CTRL_CLOSE://关闭一个线程
  48. return rt_thread_delete(thread);
  49. #endif
  50. default:
  51. break;
  52. }
  53. return RT_EOK;
  54. }

设置和删除空闲钩子

空闲钩子函数时空闲线程的钩子函数,设置空闲钩子函数可以再系统执行空闲线程时自动执行空闲钩子函数做别的事

注意:空闲线程是一个永远为就绪态的线程,因此设置的**钩子函数必须保证空闲线程在任何时刻都不会处于挂起状态,不能使用rt_thread_delay()、rt_sem_take()等函数!**

  1. rt_err_t rt_thread_idle_sethook(void (*hook)(void))//设置
  2. {
  3. rt_size_t i;
  4. rt_base_t level;
  5. rt_err_t ret = -RT_EFULL;
  6. /* disable interrupt */
  7. level = rt_hw_interrupt_disable();
  8. for (i = 0; i < RT_IDEL_HOOK_LIST_SIZE; i++)//遍历钩子列表寻找要钩的位置
  9. {
  10. if (idle_hook_list[i] == RT_NULL)
  11. {
  12. idle_hook_list[i] = hook;//将钩子函数挂到钩子列表上
  13. ret = RT_EOK;
  14. break;
  15. }
  16. }
  17. /* enable interrupt */
  18. rt_hw_interrupt_enable(level);
  19. return ret;
  20. }
  21. rt_err_t rt_thread_idle_delhook(void (*hook)(void))//删除
  22. {
  23. rt_size_t i;
  24. rt_base_t level;
  25. rt_err_t ret = -RT_ENOSYS;
  26. /* disable interrupt */
  27. level = rt_hw_interrupt_disable();
  28. for (i = 0; i < RT_IDEL_HOOK_LIST_SIZE; i++)//遍历钩子列表寻找钩子的位置
  29. {
  30. if (idle_hook_list[i] == hook)
  31. {
  32. idle_hook_list[i] = RT_NULL;//将函数脱离钩子列表
  33. ret = RT_EOK;
  34. break;
  35. }
  36. }
  37. /* enable interrupt */
  38. rt_hw_interrupt_enable(level);
  39. return ret;
  40. }

设置调度器钩子

系统的上下文切换时系统中最普遍的事件,用户想知道在一个时刻发生了什么样的线程切换,可以调用下面的函数接口设置调度器钩子,在系统线程切换时这个钩子函数将被调用

from表示系统要切换出的线程控制块指针,to表示系统要切换到的线程控制块指针

  1. rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
  2. {
  3. rt_scheduler_hook = hook;
  4. }

注意:钩子函数中基本不允许调用系统API,更不应导致当前运行的上下文挂起

钩子函数

用函数指针hook指向的函数是钩子函数的本体,在某种特殊事件下会被调用

有些类似中断,不过这个函数是纯软件的——可以形象地理解为“顺便运行的函数”

除了上面所说的钩子外还有很多其他触发条件的钩子函数

使用方法大同小异

发表评论

表情:
评论列表 (有 0 条评论,141人围观)

还没有评论,来说两句吧...

相关阅读

    相关 学习笔记——OS线

    一、线程的引入 多处理机OS中 进程回顾: 1)进程是一个拥有资源的独立单位; 2)是一个可独立调度和分派的基本单位。 进程一系列操作: 1)创建进程:分配除C

    相关 线学习笔记

    1.进程 线程 进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是执行程序的一次执行

    相关 线(IOS)学习笔记

    1、除了锁,系统还提供了条件,确保在你的应用程序任务执行的适当顺序。一个条 件作为一个看门人,阻塞给定的线程,直到它代表的条件变为真。当发生这种情况的 时候,条件释放该线程并允