0%

RT-Thread内核学习

内核各个文件

文件名 作用
kservice.c 内核库、提供c库的函数
object.c 对象管理
schedule.c 实时调度器
thread.c 线程管理
ipc.c 线程间通信
clock.c、timer.c 时钟管理
mem.c等 内存管理
device.c 设备管理

RT-Thread程序内存分布

  • code:代码段,存放程序代码
  • RO-data:只读数据段,存放程序中定义的常量 const;
  • RW-data:读写程序段,存放初始化为非0的全局变量 static
  • ZI-data:零数据段,存放未初始化的全局变量及初始化为0的变量

线程管理

功能特点

  1. 抢占式线程调度器,从就绪列表中找到优先级最高的线程,保证优先级最高的线程能够被运行,最高优先的线程一旦就绪,总能得到CPU的使用权
  2. 一个运行着的线程使一个比它优先级高的线程满足运行条件,就会让出使用权
  3. 中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行
  4. 当线程调度器线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器恢复该线程上下文信息

线程工作机制

线程控制块

​ 使用struct rt_thread表示,线程控制块是操作系统用于管理线程的数据结构;

​ 存放一些线程信息:优先级、线程名称、线程状态等;

​ 还包含线程间连接的链表结构,事件等集合。

线程重要属性
线程栈
  • 进行线程切换时,会将当前线程的上下文保存在栈中,当线程恢复运行时,再从栈中读取上下文信息,进行恢复
  • 线程栈还用于存放函数中的局部变量
线程状态
  • 初始状态
  • 就绪状态
  • 运行状态
  • 挂起状态
  • 关闭状态
线程优先级
  • 表示线程被调度的优先程度。
  • 最大支持256个优先级。在CM系列中,常用32个优先级。
  • 最低优先级的线程默认分配给空闲线程使用。
时间片
  • 每个线程都有时间片,但时间片仅对优先级相同的线程有效。
  • 系统对优先级相同的就绪状态线程采用时间片轮转的方式调度
  • 时间片起着约束线程单次运行时长的作用,单位是OS tick
线程入口函数
  • 线程控制块中entry是线程的入口函数,他是线程实现预期功能的函数。线程入口函数有用户设计实现

    • 无线循环模式

      1
      2
      3
      4
      5
      6
      7
      8
      void thread_entry(void * parameter)
      {
      while(1)
      {
      /* 等待时间发生 */
      /* 对事件进行服务、处理 */
      }
      }

      不能进入死循环模式,必须有让出CPU使用权的操作,如:循环中调用延时或者主动挂起。

    • 顺序执行或有限次循环

      1
      2
      3
      4
      static void thread_entry(void*parameter)
      {

      }

      由系统自动删除

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

  1. 线程通过调用函数 rt_thread_create/init() 进入到初始状态 RT_THREAD_INIT;

  2. 初始状态的线程通过调用函数 rt_thread_startup() 进入到就绪状态 RT_THREAD_READY;

  3. 就绪状态的线程被调度器调度后进入运行状态 RT_THREAD_RUNNING;

  4. 处于运行状态的线程调用 rt_thread_delay()、rt_sem_take()、rt_mutex_take()、rt_mb_rev()等函数或者获取不到资源时,将进入挂起状态 RT_THREAD_SUSPEND

  5. 处于挂起状态的线程,如果等待超时或者未能获得资源或由于其他线程释放了资源,那么他将回到就绪状态。调用 rt_thread_delete/detach()函数,将更改为关闭状态 RT_THREAD_CLOSE;

  6. 运行状态的线程,如果运行结束,就会在线程的最后部分执行 rt_thread_exit()函数,将状态更改为关闭状态

  7. 注意! RT-Thread中实际上不存在运行状态,就绪状态和运行状态是等同的。

系统线程

系统线程是指由系统创建的线程,用户线程是有用户程序调用线程管理接口创建的线程,在RT-Thread内核中的系统线程有空闲线程与主线程。

空闲线程

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

当系统中无其他就绪线程时,调度器将调度到空闲线程,它通常是个死循环,且永远不被挂起。

在RTT中有特殊用途:

  • 若某线程执行完毕,系统将自动删除线程:自动执行 rt_thread_exit()函数,先将该线程从系统就绪队列中删除,再将该线程状态改为关闭状态,不再参与系统调度,然后挂入 rt_thread_defunct僵尸队列(资源未回收,处于关闭状态的线程队列)中,最后空闲线程会回收被删除线程的资源。
  • 运行用户设置的钩子函数,在空闲线程运行时会调用该钩子函数,适合钩入功耗管理、看门狗喂狗等工作
主线程
  • 系统启动时,系统会创建main线程,入口函数为 main_thread_entry(),用户的应用入口函数main()从这里真正开始,用户可以在 main()函数中添加自己的初始化代码

线程管理的方式

创建动态线程 rt_thread_create() 创建静态线程 rt_thread_init()

创建和删除线程

一个线程要成为可执行的对象,就必须由操作系统的内核来为它创建一个线程。

1
2
3
4
5
6
rt_thread_t rt_thread_create(const char*name,
void (*entry)(void* parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick);

系统会从动态堆内存中分配一个线程句柄,以及按照参数中指定的栈大小分配相应的空间

参数 描述
name 线程的名称;线程名称的最大长度由rtconfig.h中的宏RT_NAME_MAX定义,多余部分会自动截掉
entry 线程入口函数
parameter 线程入口函数参数
stack_size 线程栈大小,单位是字节
priority 线程的优先级。优先级范围根据系统配置情况(rtconfig.h中的RT_THREAD_PRIORITY_MAX定义),如果支持的是256级优先级,那么范围是从0~255,数值越小优先级越高,0代表最高优先级
tick 线程时间片大小。时间片的单位是操作系统的时钟节拍。当系统中存在相同优先级的线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
返回
thread 线程创建成功,返回线程句柄
RT_NULL 线程创建失败

对于使用rt_thread_create()创建出来的线程,当不需要使用,或者运行出错是,可以使用下面的函数接口来从系统中把线程完全删除掉:

rt_err_t rt_thread_delete(rt_thread_t thread)

调用该函数后,线程对象会被移出线程队列,并且从内核对象中删除,释放掉线程占用的堆栈空间,收回来将用于其他内存分配。

注意:rt_thread_delete()是将thread变为CLOSE状态,放入rt_thread_defunct队列中;真正的删除动作(释放线程和释放线程栈)需要到下一次执行空闲线程时,由空闲线程完成最后的线程删除动作。

初始化和脱离线程
-------------THE END-------------

欢迎关注我的其它发布渠道