SylixOS 线程调度

更新时间:
2024-05-13
下载文档

SylixOS 线程调度

实时系统和分时系统的一个显著差异体现在调度策略上。实时系统调度关心的是对实时事件的响应延时,而传统的分时系统调度时要考虑的目标是多方面的:公平、效率、利用率、吞吐量等。因此实时系统通常采用优先级调度,即操作系统总是从就绪任务队列中选择最高优先级运行。

优先级调度

一旦一个线程获得处理器就独占处理器运行,除非它因某种原因决定放弃处理器,系统才会调度其他线程,这种调度方式称为“不可抢占式调度”,也就是说线程主动让出处理器后系统才重新根据优先级调度选择线程。这种调度方式会造成就绪的高优先级线程不能及时地得到响应,因此也就降低了实时响应速度。当一个线程正在运行时,操作系统可以根据某种原则剥夺已分配给它的处理器转而分配给其他线程,这种调度方式称为“可抢占式调度”,这种调度器原则有:优先级原则、时间片原则等。SylixOS 中不同优先级之间采用可抢占式的优先级调度原则。

根据上述可以看出,当注重实时响应时,应该采用“可抢占式调度”方式,只要有更高优先级线程就绪,系统立即中断当前线程来调度高优先级的线程,以确保任意时刻高优先级的线程都能得到处理器,这是实时系统的基本要求。

SylixOS 内核支持 256 个优先级:0~255。优先级 0 为最高优先级,优先级 255 为最低优先级。在线程创建时确定优先级,并允许程序运行中动态修改。但是对于内核而言,从就绪队列中选择一个线程调度时优先级是确定的,也就是说内核不会动态计算每个线程的优先级,因此这种调度策略属于静态调度策略。相对于动态调度策略调度时需要根据某个目标动态确定线程优先级并调度。静态调度策略效率高于动态调度策略。

#include <SylixOS.h>
ULONG  Lw_Thread_SetPriority(LW_OBJECT_HANDLE   ulId, UINT8   ucPriority);
ULONG  Lw_Thread_GetPriority(LW_OBJECT_HANDLE   ulId, UINT8  *pucPriority);

函数 Lw_Thread_SetPriority 原型分析:

  • 此函数成功返回 0,失败返回错误号。
  • 参数 ulId 是要设置的线程句柄。
  • 参数 ucPriority 是新的优先级值。

函数 Lw_Thread_GetPriority 原型分析:

  • 此函数成功返回 0,失败返回错误号。
  • 参数 ulId 是线程句柄。
  • 输出参数 pucPriority 返回线程优先级。

调用 Lw_Thread_SetPriority 函数可以设置一个指定线程的优先级,调用 Lw_Thread_GetPriority 函数可以获得指定线程的优先级。

我们可以使用 Shell 命令 sprio 动态修改运行线程的优先级,命令格式如下:

sprio [priority thread_id]

priority :优先级值
thread_id :目的线程ID

RR(Round-Robin)调度

前面所述的基于优先级的调度策略存在这样的问题:如果没有被更高优先级线程抢占,或者没有因阻塞等原因让出处理器,线程将一直运行下去,在此情况下,同优先级线程将得不到运行。RR 调度基于这样的哲学:在更高优先级线程调度依然优先运行的前提下,同优先级线程之间调度时追求一定意义上的公平。

RR 调度将线程运行划分为时间片,当线程运行一个时间片后,内核将其调出处理器并放在同优先级就绪线程队列尾部,重新选择下一个符合条件的线程运行。RR 调度的效果是将每个线程运行一个时间片后“让出”处理器给下一个线程,如轮转一样,所以也称轮转调度。可见,RR 调度并没有改变“基于优先级”和“可抢占”这两个实时调度的特征。

如果采用 RR 调度策略,一个值得考虑的问题是时间片大小的确定,如果时间片小有利于同优先级线程公平共享处理器,但是增加了调度开销,如果增大时间片调度开销降低,但调度效果将趋向于优先级调度。合理的时间片将是在公平和效率之间的折衷。

调用下面函数可以动态改变和获得线程的时间片。

#include <SylixOS.h>
ULONG  Lw_Thread_SetSlice(LW_OBJECT_HANDLE  ulId, UINT16  usSlice);
ULONG  Lw_Thread_GetSlice(LW_OBJECT_HANDLE  ulId, UINT16  *pusSliceTemp);
ULONG  Lw_Thread_GetSliceEx(LW_OBJECT_HANDLE  ulId, UINT16  *pusSliceTemp, 
                            UINT16           *pusCounter);

函数 Lw_Thread_SetSlice 原型分析:

  • 此函数成功返回 0,失败返回错误号。
  • 参数 ulId 是线程句柄。
  • 参数 usSlice 是线程新的时间片。

函数 Lw_Thread_GetSlice 原型分析:

  • 此函数成功返回 0,失败返回错误号。
  • 参数 ulId 是线程句柄。
  • 输出参数 pusSliceTemp 返回时间片。

函数 Lw_Thread_GetSliceEx 原型分析:

  • 此函数成功返回 0,失败返回错误号。
  • 参数 ulId 是线程句柄。
  • 输出参数 pusSliceTemp 返回时间片。
  • 输出参数 pusCounter 返回剩余的时间片。

调用 Lw_Thread_SetSlice 函数可以设置线程的运行时间片,调用 Lw_Thread_GetSlice 函数可以获得线程的时间片,调用 Lw_Thread_GetSliceEx 函数将同时获得线程所剩余的时间片。

SylixOS 中,调用下面的函数可以修改线程调度策略。

#include <SylixOS.h>
ULONG  Lw_Thread_SetSchedParam(LW_OBJECT_HANDLE  ulId,
                               UINT8             ucPolicy,
                               UINT8             ucActivatedMode);
ULONG  Lw_Thread_GetSchedParam(LW_OBJECT_HANDLE  ulId,
                               UINT8            *pucPolicy,
                               UINT8            *pucActivatedMode);

函数 Lw_Thread_SetSchedParam 原型分析:

  • 此函数成功返回 0,失败返回非 0 值。
  • 参数 ulId 是线程句柄。
  • 参数 ucPolicy 是调度策略。
  • 参数 ucActivatedMode 是响应模式,如下表所示。
响应模式说明
LW_OPTION_RESPOND_IMMIEDIA高速响应线程(仅供测试)
LW_OPTION_RESPOND_STANDARD普通响应线程
LW_OPTION_RESPOND_AUTO自动

函数 Lw_Thread_GetSchedParam 原型分析:

  • 此函数成功返回 0,失败返回非 0 值。
  • 输出参数 pucPolicy 返回调度策略。
  • 输出参数 pucActivatedMode 返回线程响应模式如上表所示。

调用 Lw_Thread_SetSchedParam 函数可以设置线程的调度策略,调用 Lw_Thread_GetSchedParam 函数可以获得线程的调度策略。

文档内容是否对您有所帮助?
有帮助
没帮助