RT-Thread内核之线程调度

Posted jzssuanfa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RT-Thread内核之线程调度相关的知识,希望对你有一定的参考价值。

4.RT-Thread中的线程?
/**
 * 线程结构
 */
struct rt_thread {
    /** Object对象 */
    char        name[RT_NAME_MAX];                      /**< 线程的名字 */
    rt_uint8_t  type;                                   /**< 对象的类型 */
    rt_uint8_t  flags;                                  /**< 线程的标志 */
#ifdef RT_USING_MODULE
    void       *module_id;                              /**< 应用模块的ID */
#endif
    rt_list_t   list;                                   /**< 对象链表 */
    rt_list_t   tlist;                                  /**< 线程链表 */
    void       *sp;                                     /**< 栈顶指针 */
    void       *entry;                                  /**< 入口函数 */
    void       *parameter;                              /**< 附加參数 */
    void       *stack_addr;                             /**< 栈底地址 */
    rt_uint16_t stack_size;                             /**< 线程栈的大小 */
    rt_err_t    error;                                  /**< 线程执行的错误码 */
    rt_uint8_t  stat;                                   /**< 线程的状态 */
    rt_uint8_t  current_priority;                       /**< 线程的当前优先级 */
    rt_uint8_t  init_priority;                          /**< 线程的初始优先级 */
 
#if RT_THREAD_PRIORITY_MAX > 32
    rt_uint8_t  number;         /**< 线程优先级相应的组号: current_priority >> 3 */
    rt_uint8_t  high_mask;      /**< 线程位号掩码: (1 << 位号) 位号: (current_priority & 7) */
#endif
    rt_uint32_t number_mask;    /**< 组号掩码: (1 << 组号) */
#if defined(RT_USING_EVENT)
    rt_uint32_t event_set;      /** 线程的事件集 */
    rt_uint8_t  event_info;     /** 多事件的逻辑关系: 与/或/非 */
#endif
    rt_ubase_t  init_tick;                              /**< 线程初始化的执行滴答值 */
    rt_ubase_t  remaining_tick;                         /**< 线程执行的剩余滴答数 */
    struct rt_timer thread_timer;                       /**< 内嵌的线程定时器 */
    void (*cleanup)(struct rt_thread *tid);             /**< 线程退出时的清除函数 */
    rt_uint32_t user_data;                              /**< 私实用户数据 */
};
总的来看。线程皆有由几类成员组成:object,栈相关信息。优先级信息,事件,定时器信息,私有数据指针。在RT-Thread中提供的线程接口函数都是环绕线程的各种状态展开的。

/*
 * 线程的状态定义
 */
#define RT_THREAD_INIT                  0x00                /**< 初始化状态 */
#define RT_THREAD_READY                 0x01                /**< 就绪状态 */
#define RT_THREAD_SUSPEND               0x02                /**< 挂起状态 */
#define RT_THREAD_RUNNING               0x03                /**< 执行状态 */
#define RT_THREAD_BLOCK                 RT_THREAD_SUSPEND   /**< 堵塞状态 */
#define RT_THREAD_CLOSE                 0x04                /**< 关闭/结束状态 */

处于初始化状态的接口函数有:rt_thread_init,rt_thread_create
这两个函数的差别在于一个是静态的初始化一个线程实体,还有一个是动态的创建线程实体再来初始化。


/*******************************************************************************************
** 函数名称: rt_thread_init
** 函数功能: 静态初始化线程实例
** 入口參数: thread 线程对象句柄
**    name 线程的名字
**    entry 线程的入口函数
**    parameter 附加參数
**    stack_start 栈底指针
**    stack_size 栈的大小
**    priority 线程的优先级
**    tick 线程的初始滴答数(能执行的时间片值)
** 返 回 值: 成功返回RT_EOK
** 调    用: rt_thread_init
*******************************************************************************************/
rt_err_t rt_thread_init(struct rt_thread *thread,
                           const char       *name,
                           void (*entry)(void *parameter),
                           void* parameter,
                           void* stack_start,
                           rt_uint32_t stack_size,
                           rt_uint8_t priority,
                           rt_uint32_t tick)
{
    /** 參数检查 */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(stack_start != RT_NULL);
    /** 初始化线程内嵌的对象结构 */
    rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);
    /** 初始化线程实例 */
    return _rt_thread_init(thread,
                           name,
                           entry,
                           parameter,
                           stack_start,
                           stack_size,
                           priority,
                           tick);
}


/*******************************************************************************************
** 函数名称: rt_thread_create
** 函数功能: 动态的创建线程
** 入口參数: name 线程的名字
**    entry 线程的入口
**    parameter 附加參数
**    stack_size 线程栈的大小
**    priority 线程的优先级
**    tick 线程的初始化滴答数
** 返 回 值: 线程对象句柄
** 调    用:
*******************************************************************************************/
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)
{
    struct rt_thread *thread;
    void *stack_start;
    /** 分配线程对象 */
    thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name);
    if (thread == RT_NULL)
        return RT_NULL;
    /** 分配线程的栈 */
    stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
    if (stack_start == RT_NULL) {
        rt_object_delete((rt_object_t)thread);
        return RT_NULL;
    }
    /** 初始化线程实例 */
    _rt_thread_init(thread,
                    name,
                    entry,
                    parameter,
                    stack_start,
                    stack_size,
                    priority,
                    tick);
    return thread;
}

终于都调用到_rt_thread_init函数:
/******************************************************************************************* 
** 函数名称: _rt_thread_init
** 函数功能: 初始化线程实例
** 入口參数: thread 线程对象句柄
**    name 线程的名字
**    entry 线程的入口函数
**    parameter 附加參数
**    stack_start 栈底指针
**    stack_size 栈的大小
**    priority 线程的优先级
**    tick 线程的初始滴答数(能执行的时间片值)
** 返 回 值: 成功返回RT_EOK
** 调    用: rt_thread_init
*******************************************************************************************/
static rt_err_t _rt_thread_init(struct rt_thread* thread,
                                 const char* name,
                                 void (*entry)(void *parameter),
                                 void * parameter,
                                 void * stack_start,
                                 rt_uint32_t stack_size,
                                 rt_uint8_t  priority,
                                 rt_uint32_t tick)
{
    /** 初始化线程链表节点成员 */
    rt_list_init(&(thread->tlist));
    thread->entry = (void *)entry;
    thread->parameter = parameter;
    thread->stack_addr = stack_start;
    thread->stack_size = (rt_uint16_t)stack_size;
    /** 初始化线程的栈 */
    rt_memset(thread->stack_addr, ‘#‘, thread->stack_size);
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
        (void *)((char *)thread->stack_addr + thread->stack_size - 4),
        (void *)rt_thread_exit);
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;
    thread->current_priority = priority;
    thread->init_tick      = tick;
    thread->remaining_tick = tick;
    thread->error = RT_EOK;
 
    /** 创建线程时,线程处于INIT状态 */
    thread->stat  = RT_THREAD_INIT;
    thread->cleanup   = 0;
    thread->user_data = 0;
    /** 初始化线程内嵌的定时器 */
    rt_timer_init(&(thread->thread_timer),
                  thread->name,
                  rt_thread_timeout,
                  thread,
                  0,
                  RT_TIMER_FLAG_ONE_SHOT);
    return RT_EOK;
}
在这个函数中有几点须要注意:
1.线程的栈是怎样初始化的?
线程的栈是这样布局的:
-----------
| Entry   |        <--- 线程的入口函数
-----------    
|  LR     |        <--- 返回地址
-----------
|  R12    |        <--- 通用寄存器
-----------
|  R11    |
-----------
|  R10    |
-----------
|  R9     |
-----------
|  R8     |
-----------
|  R7     |
-----------
|  R6     |
-----------
|  R5     |
-----------
|  R4     |
-----------
|  R3     |
-----------
|  R2     |
-----------
|  R1     |
-----------
|  R0     |
-----------
|  CPSR   |    <--- 当前程序状态寄存器
-----------    
|  SPSR   |    <--- 保存程序状态寄存器
-----------

2.线程内嵌的定时器的目的?
线程内嵌定时器来实现线程状态的改变。比方当须要让线程休眠多少个时钟滴答时,能够将线程设置为挂起状态。然后设置定时器的超时时间启动定时器。这样当定时器超市就能唤醒线程

3.此时线程是否具备了执行条件?
初始化后线程的状态为INIT状态,并不能执行,要转换成就绪状态需调用rt_thread_startup


设置线程为就绪状态的接口函数有rt_thread_control。rt_thread_resume。rt_thread_timeout
rt_thread_control
    rt_schedule_insert_thread
        thread->stat = RT_THREAD_READY


rt_thread_resume
    rt_schedule_insert_thread
        thread->stat = RT_THREAD_READY

rt_thread_timeout
    rt_schedule_insert_thread
        thread->stat = RT_THREAD_READY
能够发现设置线程为就绪状态的接口函数实际为rt_schedule_insert_thread
/*********************************************************************************************************
** 函数名称: rt_schedule_insert_thread
** 函数功能: 将线程插入到就绪队列中
** 入口參数: thread 线程对象句柄
** 返 回 值: 无
** 调    用:
*********************************************************************************************************/
void rt_schedule_insert_thread(struct rt_thread* thread)
{
    register rt_base_t temp;
    /** 參数检查 */
    RT_ASSERT(thread != RT_NULL);
    /** 禁止全局中断 */
    temp = rt_hw_interrupt_disable();
    /** 设置线程的状态为就绪态 */
    thread->stat = RT_THREAD_READY;
    /** 将线程插入到优先级队列中 */
    rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
                          &(thread->tlist));
#if RT_THREAD_PRIORITY_MAX <= 32
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name, thread->current_priority));
#else
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                 ("insert thread[%.*s], the priority: %d 0x%x %d\n",
                  RT_NAME_MAX,
                  thread->name,
                  thread->number,
                  thread->number_mask,
                  thread->high_mask));
#endif
    /** 设置就绪表和就绪组 */
#if RT_THREAD_PRIORITY_MAX > 32
    rt_thread_ready_table[thread->number] |= thread->high_mask;
#endif
    rt_thread_ready_priority_group |= thread->number_mask;
    /** 使能全局中断 */
    rt_hw_interrupt_enable(temp);
}
在函数中除了将线程设置为就绪状态外,还有几个动作须要关注:
1.将线程增加线程优先级链表中。在RT-Thread支持的优先级个数能够为32或256,但系统所支持的线程数目并没有限制。

因此,处于同一优先级的线程是能够多个的,这些线程将以链表连接起来。这些具有同样优先级的线程的调度方式为时间片调度。

rt_thread_priority_table[256]
----------
|   0    |----->线程2 ---> 线程5 --->...
----------
|   1    |
----------
.........
----------
|   253  | ---> 线程100 --->线程3
----------
|   254  |
----------
|   255  | ---> 线程50
----------

2.设置了全局的就绪表和就绪组


设置线程为挂起状态的接口函数有rt_thread_startup,rt_thread_suspend


rt_thread_startup
    thread->stat = RT_THREAD_SUSPEND        /** 设置线程的状态为挂起状态 */

rt_thread_suspend
    thread->stat = RT_THREAD_SUSPEND
    rt_schedule_remove_thread

设置线程为挂起状态的接口函数有rt_thread_exit。rt_thread_detach,rt_thread_delete
rt_thread_delete
    rt_schedule_remove_thread
    thread->stat = RT_THREAD_CLOSE
    rt_list_insert_after(&rt_thread_defunct, &(thread->tlist))

rt_thread_detach
    rt_schedule_remove_thread
    rt_timer_detach
    thread->stat = RT_THREAD_CLOSE
    rt_object_detach

rt_thread_exit
    rt_schedule_remove_thread
    thread->stat = RT_THREAD_CLOSE
    rt_timer_detach

以上是关于RT-Thread内核之线程调度的主要内容,如果未能解决你的问题,请参考以下文章

RT-Thread内核线程调度算法(基于位图的线程调度算法)

RT-Thread--内核基础

RT-Thread 线程调度

RT-Thread相同优先级线程的调度

[RTT] RT-Thread线程调度机制线程切换时机

RT-Thread(RTOS)之初试线程