SylixOS线程调度浅析

  1. 概念

    SylixOS中,在就绪线程和运行线程之间还存在候选运行线程。线程就绪后会被放置到就绪表中,而最需要运行(优先级最高)的线程会被放置到候选表,正常情况下CPU当前任务结束后,会运行候选表里的线程。

  2. 就绪表

    就绪表存放了SylixOS中除了"候选表中的线程"外,所有就绪没运行的线程。

  3. 候选表

    每一个CPU的结构体里面都有一个候选运行表,每一个候选表里最多有一个候选运行线程。候选运行表结构如程序清单 11所示。

    程序清单 11候选运行表结构

    /*********************************************************************************************************
      候选运行表结构
    *********************************************************************************************************/
    #ifdef __SYLIXOS_KERNEL
    
    typedef struct {
        volatile PLW_CLASS_TCB  CAND_ptcbCand;                              /*  候选运行线程                */
        volatile BOOL           CAND_bNeedRotate;                           /*  可能产生了优先级卷绕        */
    } LW_CLASS_CAND;
    typedef LW_CLASS_CAND      *PLW_CLASS_CAND;
  4. 优先级卷绕

    如图 11所示,优先级卷绕是CPU结构体里面的候选表里的标志位CAND_bNeedRotate。

    11候选运行表基本操作

  5. 优先级卷绕的产生

    当候选表不为空时,有一个优先级高于"候选表中线程"的线程就绪时,会产生优先级卷绕并将CPU的优先级卷绕标志位设置为1。具体流程如图 12所示。

    12优先级卷绕产生的流程

     

  6. 线程调度

    系统主要在退出内核和退出中断时尝试进行线程调度。尝试进行线程调度的主要函数有:__kernelExit函数、__kernelExitIrq函数、__kernelSched函数和__kernelSchedInt函数。这里以__kernelExit函数为例介绍线程调度。

  7. 线程调度流程

    SylixOS线程调度流程如图 21所示。

    21线程调度流程

  8. 尝试调度

    尝试调度,检查当前执行线程能否调度。(中断中或者在内核中执行, 不允许调度;当前线程就绪且被锁定, 不允许调度。)如程序清单 21所示。

    1. 如果当前执行线程不能调度,继续运行当前线程,不产生调度。
    2. 如果当前执行线程能调度,继续判断是否产生了优先级卷绕。

    程序清单 21 _SchedGetCand函数 

    PLW_CLASS_TCB  _SchedGetCand (PLW_CLASS_CPU  pcpuCur, ULONG  ulCurMaxLock)
    {
        if (!__COULD_SCHED(pcpuCur, ulCurMaxLock)) {                        /*  当前执行线程不能调度        */
            return  (pcpuCur->CPU_ptcbTCBCur);
            
        } else {                                                            /*  可以执行线程切换            */
            if (LW_CAND_ROT(pcpuCur)) {                                     /*  判断是否产生优先级卷绕      */
                _CandTableUpdate(pcpuCur);
            }
            return  (LW_CAND_TCB(pcpuCur));
        }
    }
    

  9. 优先级卷绕处理

    优先级卷绕的处理,如程序清单 22所示。

    第一步,如果产生了优先级卷绕,判断候选表是否为空。

    第二步,如果候选表不为空,判断就绪线程是否存在更加需要运行的线程。

    第三步,如果存在更加需要运行的线程,清空候选表(被清空的线程,会被插到对应就绪表的头部,下次优先调用),重新选择一个更需要运行的线程放入候选表,进行线程上下文切换,执行候选表中线程。

    程序清单 22优先级就卷绕处理函数

    VOID _CandTableUpdate (PLW_CLASS_CPU   pcpu)
    {
                 UINT8              ucPriority;
        REGISTER PLW_CLASS_TCB      ptcbCand;
                 PLW_CLASS_PCBBMAP  ppcbbmap;
                 BOOL               bNeedRotate = LW_FALSE;
    
        if (!LW_CPU_IS_ACTIVE(pcpu)) {                                      /*  CPU 必须为激活状态          */
            return;
        }
        
        ptcbCand = LW_CAND_TCB(pcpu);
        if (ptcbCand == LW_NULL) {                                          /*  当前没有候选线程            */
            _CandTableFill(pcpu);
            goto    __update_done;
        }
        
        ppcbbmap = _SchedSeekPriority(pcpu, &ucPriority);                   /*  当前就绪表中最高优先级      */
        if (ppcbbmap == LW_NULL) {
            LW_CAND_ROT(pcpu) = LW_FALSE;                                   /*  清除优先级卷绕标志          */
            return;
        }
        
        if (ptcbCand->TCB_usSchedCounter == 0) {                            /*  已经没有时间片了            */
            if (LW_PRIO_IS_HIGH_OR_EQU(ucPriority, 
                                       ptcbCand->TCB_ucPriority)) {         /*  是否需要轮转                */
                bNeedRotate = LW_TRUE;
            }
        } else {
            if (LW_PRIO_IS_HIGH(ucPriority, 
                                ptcbCand->TCB_ucPriority)) {
                bNeedRotate = LW_TRUE;
            }
        }
        
        if (bNeedRotate) {                                                  /*  存在更需要运行的线程        */
            _CandTableEmpty(pcpu);                                          /*  清空候选表                  */
            _CandTableResel(pcpu, ppcbbmap, ucPriority);                    /*  重新选择任务执行            */
        }
        
    __update_done:
        LW_CAND_ROT(pcpu) = LW_FALSE;                                       /*  清除优先级卷绕标志          */
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal 是 Java 中的一个类,它提供了一种线程局部变量的机制。线程局部变量是指每个线程都有自己的变量副本,每个线程对该变量的访问都是独立的,互不影响。 ThreadLocal 主要用于解决多线程并发访问共享变量时的线程安全问题。在多线程环境下,如果多个线程共同访问同一个变量,可能会出现竞争条件,导致数据不一致或者出现线程安全问题。通过使用 ThreadLocal,可以为每个线程提供独立的副本,从而避免了线程安全问题。 ThreadLocal 的工作原理是,每个 Thread 对象内部都维护了一个 ThreadLocalMap 对象,ThreadLocalMap 是一个 key-value 结构,其中 key 是 ThreadLocal 对象,value 是该线程对应的变量副本。当访问 ThreadLocal 的 get() 方法时,会根据当前线程获取到对应的 ThreadLocalMap 对象,并从中查找到与 ThreadLocal 对象对应的值。如果当前线程尚未设置该 ThreadLocal 对象的值,则会通过 initialValue() 方法初始化一个值,并将其存入 ThreadLocalMap 中。当访问 ThreadLocal 的 set() 方法时,会将指定的值存入当前线程对应的 ThreadLocalMap 中。 需要注意的是,ThreadLocal 并不能解决共享资源的并发访问问题,它只是提供了一种线程内部的隔离机制。在使用 ThreadLocal 时,需要注意合理地使用,避免出现内存泄漏或者数据不一致的情况。另外,由于 ThreadLocal 使用了线程的 ThreadLocalMap,因此在使用完 ThreadLocal 后,需要手动调用 remove() 方法清理对应的变量副本,以防止内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值