美文网首页
RTT笔记-线程调度

RTT笔记-线程调度

作者: lissettecarlr | 来源:发表于2021-02-12 00:12 被阅读0次

承接上文分析完线程,此文研究线程调度,由初始化函数rt_system_scheduler_init开始。

scheduler.c文件

void rt_system_scheduler_init(void)
{
#ifdef RT_USING_SMP//多核宏,暂不管
    int cpu;
#endif /*RT_USING_SMP*/
    register rt_base_t offset;

#ifndef RT_USING_SMP
    rt_scheduler_lock_nest = 0;
#endif /*RT_USING_SMP*/

    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02x\n",
                                      RT_THREAD_PRIORITY_MAX));//优先级在rtconfig.h中被定义

    for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
    {
              //rt_thread_priority_table被用来实现优先级的管理,是一个双向链表,这里初始化是将链表指针置空
        rt_list_init(&rt_thread_priority_table[offset]);
    }
#ifdef RT_USING_SMP
    for (cpu = 0; cpu < RT_CPUS_NR; cpu++)
    {
        struct rt_cpu *pcpu =  rt_cpu_index(cpu);
        for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
        {
            rt_list_init(&pcpu->priority_table[offset]);
        }

        pcpu->irq_switch_flag = 0;
        pcpu->current_priority = RT_THREAD_PRIORITY_MAX - 1;
        pcpu->current_thread = RT_NULL;
        pcpu->priority_group = 0;

#if RT_THREAD_PRIORITY_MAX > 32
        rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table));
#endif
    }
#endif /*RT_USING_SMP*/

    /* initialize ready priority group */
    rt_thread_ready_priority_group = 0;

#if RT_THREAD_PRIORITY_MAX > 32
    /* initialize ready table */
    rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif

    /* initialize thread defunct */
    rt_list_init(&rt_thread_defunct);//初始化空闲回调的链表
}

该函数仅仅是在将之后需要的变量赋初值,实际启动调度管理的函数被执行在components.c中rtthread_startup函数的最后一步。如其名启动调度器rt_system_scheduler_start

void rt_system_scheduler_start(void)
{
    register struct rt_thread *to_thread;
    rt_ubase_t highest_ready_priority;
 
      //获取就绪队列中优先级最高的线程
    to_thread = _get_highest_priority_thread(&highest_ready_priority);

#ifdef RT_USING_SMP
    to_thread->oncpu = rt_hw_cpu_id();
#else
    rt_current_thread = to_thread;//将当前运行线程指针指向这个即将运行的线程
#endif /*RT_USING_SMP*/

    rt_schedule_remove_thread(to_thread);//将该线程从线程调度器中移除
      to_thread->stat = RT_THREAD_RUNNING;//切换为正在运行模式

    /* switch to new thread */
#ifdef RT_USING_SMP
    rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread);
#else
    rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp);//通过堆栈寄存器指针调整运行该进程
#endif /*RT_USING_SMP*/

    /* never come back */
}

该函数很直白,就是找到就绪队列中最高优先级的线程,然后跳转运行。这里是用来启动第一个线程,仅会被执行一次。其中很多变量是在线程中被定义的,可以看前一篇文章了解。下文将先阅读线程启动函数中用到一些功能性函数。

首先是获取最高优先级线程指针的函数

static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio)
{
    register struct rt_thread *highest_priority_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 highest ready priority thread */
      //通过得到的最高优先级 找到最近的一个线程
    highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);

    *highest_prio = highest_ready_priority;//保存该优先级

    return highest_priority_thread;
}

这里补充一下优先级数组的存储方式,rt_thread_priority_table的每一个成员代表一种优先级,而每个成员是一张链表,则保存在该优先级上的所有线程。


image.png

这里就是通过找到最高优先级,然后在找到该优先级下最近指向的线程来找到应该被执行的线程。
接下来再看看关于线程加入调度器的代码。

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();

    /* it's current thread, it should be RUNNING thread */
      //这里判断该进程是否是正在运行,如果是,则表明进程状态出错,将其修改后退出
    if (thread == rt_current_thread)
    {
        thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK);
        goto __exit;
    }

    /* READY thread, insert to ready queue 将线程状态切换为就绪*/
    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));

    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name, thread->current_priority));

    /* set priority mask */
#if RT_THREAD_PRIORITY_MAX > 32
    rt_thread_ready_table[thread->number] |= thread->high_mask;
#endif
        //更新优先级就绪情况变量,该变量用来展示那些优先级中有线程
    rt_thread_ready_priority_group |= thread->number_mask;

__exit:
    /* enable interrupt */
    rt_hw_interrupt_enable(temp);
}

然后是将线程移除调度器函数。

void rt_schedule_remove_thread(struct rt_thread *thread)
{
    register rt_base_t level;

    RT_ASSERT(thread != RT_NULL);

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

    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name,
                                      thread->current_priority));

    /* remove thread from ready list */
    rt_list_remove(&(thread->tlist));//将该节点从链表中删除
      //判断该优先级上是否还有线程,如果没用则需要更新优先级线程存在情况变量
    if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority])))
    {
#if RT_THREAD_PRIORITY_MAX > 32
        rt_thread_ready_table[thread->number] &= ~thread->high_mask;
        if (rt_thread_ready_table[thread->number] == 0)
        {
            rt_thread_ready_priority_group &= ~thread->number_mask;
        }
#else
        rt_thread_ready_priority_group &= ~thread->number_mask;
#endif
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}

线程切换实际是通过rt_schedule函数来执行的,在任何地方通过执行它系统便从调度器中找出最高优先级的进程,通过比对当前执行中的线程优先级判断是否切换。

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) //确保没用被上锁
    {
        rt_ubase_t highest_ready_priority;

        if (rt_thread_ready_priority_group != 0)//确保在调度器中有线程
        {
            /* need_insert_from_thread: need to insert from_thread to ready queue */
            int need_insert_from_thread = 0;

                      //得到一个最高优先级的进程
            to_thread = _get_highest_priority_thread(&highest_ready_priority);

            if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)//如果当前有线程正在运行
            {
                if (rt_current_thread->current_priority < highest_ready_priority) //如果当前正在运行的线程优先级更高
                {
                    to_thread = rt_current_thread;
                }
                else
                {
                    need_insert_from_thread = 1;
                }
            }

            if (to_thread != rt_current_thread) //所得线程并不是正在运行的线程
            {
                /* if the destination thread is not the same as 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));

                if (need_insert_from_thread)
                {
                                      //将当前正在运行的线程回存入调度器
                    rt_schedule_insert_thread(from_thread);
                }
                //将即将运行的线程从调度器中移除
                rt_schedule_remove_thread(to_thread);
                to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);//切换为运行态

                /* switch to new thread */
                RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                        ("[%d]switch to priority#%d "
                         "thread:%.*s(sp:0x%08x), "
                         "from thread:%.*s(sp: 0x%08x)\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)//如果没有中断嵌套
                {
                    extern void rt_thread_handle_sig(rt_bool_t clean_state);
                    //线程切换
                    rt_hw_context_switch((rt_ubase_t)&from_thread->sp,
                            (rt_ubase_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
                    {
                        rt_hw_interrupt_enable(level);
                    }
#else
                    /* enable interrupt */
                    rt_hw_interrupt_enable(level);
#endif
                    goto __exit;
                }
                else
                {
                    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
                    //线程切换
                    rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
                            (rt_ubase_t)&to_thread->sp);
                }
            }
            else//所得线程即是正在运行线程
            {
                rt_schedule_remove_thread(rt_current_thread);
                rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK);
            }
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

__exit:
    return;
}

至于系统是如何自动执行线程切换的,就需要回到之前提到的线程部分,每个线程都有一个定时器,用于根据设定的时间片配置闹钟,当超时后会调用统一的超时函数rt_thread_timeout,在该函数中变调用了rt_schedule来实现线程切换。最终呈现出来的效果就是:
rt_system_scheduler_start启动第一个线程
第一个线程时间片耗尽,进入超时函数,切换线程,循环往复。在不加入新线程的情况下,就这样有序的从高到低优先级依次执行线程。至于高优先级打断低优先级的情况必然是当即执行了线程切换函数的,例如在新添加进程的函数中便执行了rt_schedule,通过全局查询rt_schedule便知道很多地方也调用到。

相关文章

  • RTT笔记-线程调度

    承接上文分析完线程,此文研究线程调度,由初始化函数rt_system_scheduler_init开始。 sche...

  • RTT笔记-线程

    继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数 然后...

  • 2022-07-26_Jdk调度线程服务ScheduledThr

    20220726_Jdk调度线程服务ScheduledThreadPoolExecutor学习笔记.md 1概述 ...

  • Golang 学习笔记(06)—— 多线程

    本文为转载,原文:Golang 学习笔记(06)—— 多线程 介绍 线程是cpu调度的最小单位,只有不同的线程才能...

  • java虚拟机读书笔记之线程调度

    java线程调度 线程调度主要有两种方式,协同式线程调度和抢占式线程调度。1、协同式: 线程的执行时间由线程本身...

  • RxJava的学习笔记(三)线程调度

    前言 RxJava的学习笔记(一)基础概念RxJava的学习笔记(二)操作符RxJava的学习笔记(三)线程调度上...

  • RxJava的学习笔记(一)基础概念

    前言 RxJava的学习笔记(一)基础概念RxJava的学习笔记(二)操作符RxJava的学习笔记(三)线程调度去...

  • RxJava的学习笔记(二)操作符

    前言 RxJava的学习笔记(一)基础概念RxJava的学习笔记(二)操作符RxJava的学习笔记(三)线程调度上...

  • [Java]线程和锁

    0x00 线程调度 线程调度指的是系统为线程分配CPU使用权。分为两种: 协同式线程调度线程想用CPU多久就用多久...

  • CPU调度

    CPU调度 基本概念 CPU调度在讨论普通调度概念时使用进程调度,特别指定为线程概念时使用线程调度 CPU-I/O...

网友评论

      本文标题:RTT笔记-线程调度

      本文链接:https://www.haomeiwen.com/subject/qozzsctx.html