RT-Thread源码-3-线程调度函数剖析

初始

  • 在RT-Thread的启动流程中,rtthread_startup的最后一步,调用了rt_system_scheduler_start开启了线程调度器,此函数源码如下:
void rt_system_scheduler_start(void)
{
    register struct rt_thread *to_thread;
    register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX > 32
    register rt_ubase_t number;

    number = __rt_ffs(rt_thread_ready_priority_group) - 1;
    highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif

    /* get switch to thread */
    to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);

    rt_current_thread = to_thread;

    /* switch to new thread */
    rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);

    /* never come back */
}
  1. 代码中,无论RT_THREAD_PRIORITY_MAX设置的最大优先级是否大于32,都会调用__rt_ffs从就绪线程的优先级分组中选择当前处于就绪态并且优先级最高的线程索引,关于__rt_ffs函数剖析见此处
  2. 这里若最大线程数不超过32,即最大线程优先级不超过32,则会用一个32bit的变量,其中每一个bit表示每一个线程的状态,若线程为就绪态则为1,挂起态则为0;反之,会使用一个长度为32且每个元素为8bit变量的数组以及一个32bit变量用来分组。支持最大线程数为256。
#if RT_THREAD_PRIORITY_MAX > 32
/* Maximum priority level, 256 */
rt_uint32_t rt_thread_ready_priority_group;
rt_uint8_t rt_thread_ready_table[32];
#else
/* Maximum priority level, 32 */
rt_uint32_t rt_thread_ready_priority_group;
#endif

运行

  • rt_schedule实现如下:
/**
 * This function will perform one schedule. It will select one thread
 * with the highest priority level, then switch to it.
 */
void rt_schedule(void)
{
    rt_base_t level;
    struct rt_thread *to_thread;
    struct rt_thread *from_thread;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /* check the scheduler is enabled or not */
    if (rt_scheduler_lock_nest == 0)
    {
        register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX <= 32
        highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#else
        register rt_ubase_t number;

        number = __rt_ffs(rt_thread_ready_priority_group) - 1;
        highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#endif

        /* get switch to thread */
        to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                                  struct rt_thread,
                                  tlist);

        /* if the destination thread is not the same as current thread */
        if (to_thread != rt_current_thread)
        {
            rt_current_priority = (rt_uint8_t)highest_ready_priority;
            from_thread         = rt_current_thread;
            rt_current_thread   = to_thread;

            RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));

            /* switch to new thread */
            RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                         ("[%d]switch to priority#%d "
                          "thread:%.*s(sp:0x%p), "
                          "from thread:%.*s(sp: 0x%p)\n",
                          rt_interrupt_nest, highest_ready_priority,
                          RT_NAME_MAX, to_thread->name, to_thread->sp,
                          RT_NAME_MAX, from_thread->name, from_thread->sp));

#ifdef RT_USING_OVERFLOW_CHECK
            _rt_scheduler_stack_check(to_thread);
#endif

            if (rt_interrupt_nest == 0)
            {
                rt_hw_context_switch((rt_uint32_t)&from_thread->sp,
                                     (rt_uint32_t)&to_thread->sp);

#ifdef RT_USING_SIGNALS
                if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING)
                {
                    extern void rt_thread_handle_sig(rt_bool_t clean_state);

                    rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;

                    rt_hw_interrupt_enable(level);

                    /* check signal status */
                    rt_thread_handle_sig(RT_TRUE);
                }
                else
#endif
                {
                    /* enable interrupt */
                    rt_hw_interrupt_enable(level);
                }

                return ;
            }
            else
            {
                RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));

                rt_hw_context_switch_interrupt((rt_uint32_t)&from_thread->sp,
                                               (rt_uint32_t)&to_thread->sp);
            }
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}
  • 此函数执行逻辑如下
  1. 失能全局中断
  2. 判断当前是否处于临界区中,若处于临界区,则不进行任何操作,使能全局中断后退出。
  3. 若未处于临界区,则先执行调度钩子函数,之后搜索出已就绪最高优先级的线程,若此线程为当前正在执行的线程,则不进行任何操作,使能全局中断后退出。反之,则进行一次上下文切换,这里,若未处于中断中,除了执行一次上下文切换外,若使能了信号且当前线程处于等待信号的状态,则会进行一次信号处理。
  • rt_schedule_insert_thread 实现如下:
/*
 * This function will insert a thread to system ready queue. The state of
 * thread will be set as READY and remove from suspend queue.
 *
 * @param thread the thread to be inserted
 * @note Please do not invoke this function in user application.
 */
void rt_schedule_insert_thread(struct rt_thread *thread)
{
    register rt_base_t temp;

    RT_ASSERT(thread != RT_NULL);

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    /* change stat */
    thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK);

    /* insert thread to ready list */
    rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
                          &(thread->tlist));

    /* set priority mask */
#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;

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);
}
  • 此函数主要用于将线程插入于就绪列表中,并且更新其状态为就绪态以及更新线程就绪位变量,便于下一次已就绪最高优先级线程索引的获取。
  • rt_schedule_remove_thread 此函数执行功能与上述相反,此处不做详细说明,总而言之,除了对就绪线程列表的更新,就是对线程就绪位变量的更新。

临界区操作

  • 临界区操作,个人理解是对调度器的锁控制,若进入临界区,则表示在退出临界区前无法执行线程调度。临界区操作,包括rt_enter_criticalrt_exit_criticalrt_critical_level。这里使用方法同rt_interrupt_enterrt_interrupt_leave以及rt_hw_interrupt_enablert_hw_interrupt_disablert_enter_criticalrt_exit_critical的调用在代码中必须成对出现,否则会导致调度器锁死。
  • rt_enter_critical函数实现如下:
/**
 * This function will lock the thread scheduler.
 */
void rt_enter_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */
    rt_scheduler_lock_nest ++;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}
  • rt_exit_critical函数实现如下:
/**
 * This function will unlock the thread scheduler.
 */
void rt_exit_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_scheduler_lock_nest --;
    if (rt_scheduler_lock_nest <= 0)
    {
        rt_scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        if (rt_current_thread)
        {
            /* if scheduler is started, do a schedule */
            rt_schedule();
        }
    }
    else
    {
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }
}
  • 这里在rt_scheduler_lock_nest为0时,会对当前线程是否存在进行判断,若不存在,则表示当前可能处于调度器初始化状态,不执行调度操作。
  • rt_critical_level 通过此函数能够获取当前进入临界区的嵌套数。
  • 另外,在执行中断服务时会需要调用rt_interrupt_enter函数,此函数支持嵌套,执行它表示当前存在中断服务执行,此时即使没有进入临界区,也会将调度器上锁;在完成中断服务时会需要调用rt_interrupt_leave函数,若不调用此函数,将不会启动线程调度。

你可能感兴趣的:(RTOS,arm,mcu)