修订历史
版本 日期 原因
V1.00 2017/01/20 创建文档
目 录
1. 概念 1
1.1 就绪表 1
1.2 候选表 1
1.3 优先级卷绕 1
1.3.1 优先级卷绕的产生 1
2. 线程调度 2
2.1 线程调度流程 2
2.1.1 尝试调度 3
2.1.2 优先级卷绕处理 4
1. 概念
在SylixOS中,在就绪线程和运行线程之间还存在候选运行线程。线程就绪后会被放置到就绪表中,而需要运行(优先级高)的线程会被放置到候选表,正常情况下CPU当前任务结束后,会运行候选表里的线程。
1.1 就绪表
就绪表存放了SylixOS中除了“候选表中的线程”外,所有就绪没运行的线程。
1.2 候选表
每一个CPU的结构体里面都有一个候选运行表,每一个候选表里多有一个候选运行线程。候选运行表结构如程序清单 1 1所示。
程序清单 1 1 候选运行表结构
/*********************************************************************************************************
候选运行表结构
*********************************************************************************************************/
#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;
1.3 优先级卷绕
优先级卷绕是CPU结构体里面的候选表里的标志位CAND_bNeedRotate。
1.3.1 优先级卷绕的产生
当候选表不为空时,有一个优先级高于“候选表中线程”的线程就绪时,会产生优先级卷绕并将CPU的优先级卷绕标志位设置为1。
2. 线程调度
系统主要在退出内核和退出中断时尝试进行线程调度。尝试进行线程调度的主要函数有:__kernelExit函数、__kernelExitIrq函数、__kernelSched函数和__kernelSchedInt函数。这里以__kernelExit函数为例介绍线程调度。
2.1 线程调度流程
2.1.1 尝试调度
尝试调度,检查当前执行线程能否调度。(中断中或者在内核中执行, 不允许调度;当前线程就绪且被锁定, 不允许调度。)如程序清单 2 1所示。
1) 如果当前执行线程不能调度,继续运行当前线程,不产生调度。
2) 如果当前执行线程能调度,继续判断是否产生了优先级卷绕。
程序清单 2 1 _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));
}
}
2.1.2 优先级卷绕处理
优先级卷绕的处理,如程序清单 2 2所示。
第一步,如果产生了优先级卷绕,判断候选表是否为空。
第二步,如果候选表不为空,判断就绪线程是否存在更加需要运行的线程。
第三步,如果存在更加需要运行的线程,清空候选表(被清空的线程,会被插到对应就绪表的头部,下次优先调用),重新选择一个更需要运行的线程放入候选表,进行线程上下文切换,执行候选表中线程。
程序清单 2 2 优先级就卷绕处理函数
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; /* 清除优先级卷绕标志 */
}