RMS 调度
为周期性任务解决多任务调度冲突的一种非常好的方法是速率单调调度(Rate Monotonic Scheduling RMS),RMS 基于任务的周期指定优先级。
在 RMS 中,最短周期的任务具有最高优先级,次短周期的任务具有次高优先级,依次类推。当同时有多个任务可以被执行时,最短周期的任务被优先执行。如果将任务的优先级视为速率的函数,那么这就是一个单调递增函数。
#include <SylixOS.h>
LW_HANDLE Lw_Rms_Create(CPCHAR pcName,
ULONG ulOption,
LW_OBJECT_ID *pulId);
ULONG Lw_Rms_Delete(LW_HANDLE *pulId);
ULONG Lw_Rms_DeleteEx(LW_HANDLE *pulId,
BOOL bForce);
ULONG Lw_Rms_Cancel(LW_HANDLE ulId);
函数 Lw_Rms_Create 原型分析:
- 此函数成功返回 RMS 句柄,失败返回 LW_HANDLE_INVALID 并设置错误号。
- 参数 pcName 是 RMS 名字。
- 参数 ulOption 是 RMS 选项。
- 输出参数 pulId 返回 RMS 句柄。
函数 Lw_Rms_Delete 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pulId 是 RMS 句柄指针。
函数 Lw_Rms_DeleteEx 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pulId 是 RMS 句柄指针。
- 参数 bForce 是删除类型。
函数 Lw_Rms_Cancel 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 ulId 是 RMS 句柄。
调用 Lw_Rms_Create 函数可以创建一个 RMS 调度器;调用 Lw_Rms_Delete 函数可以删除一个 RMS 调度器,需要注意的是,如果 RMS 调度器处于有任务阻塞状态,则不会删除 RMS 对象并置 errno 为 ERROR_RMS_STATUS;调用 Lw_Rms_DeleteEx 函数可以删除一个 RMS 调度器,与 Lw_Rms_Delete 函数不同的是,如果 bForce 为 true,则无论 RMS 调度器处于什么状态都会删除,否则,行为与 Lw_Rms_Delete 函数相同;调用 Lw_Rms_Cancel 函数将使指定的 RMS 调度器停止工作,但不会删除 RMS 对象。
#include <SylixOS.h>
ULONG Lw_Rms_Period(LW_HANDLE ulId,
ULONG ulPeriod);
ULONG Lw_Rms_ExecTimeGet(LW_HANDLE *pulId,
ULONG *pulExecTime);
函数 Lw_Rms_Period 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 ulId 是 RMS 句柄。
- 参数 ulPeriod 是程序执行周期。
函数 Lw_Rms_ExecTimeGet 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 ulId 是 RMS 句柄。
- 输出参数 pulExecTime 返回运行时间。
调用 Lw_Rms_Period 函数后 RMS 调度器将按照参数 ulPeriod 指定的周期开始工作;调用 Lw_Rms_ExecTimeGet 函数将获得从 Lw_Rms_Period 函数调用开始到目前执行的时间(单位:Tick)。
#include <SylixOS.h>
ULONG Lw_Rms_Status(LW_HANDLE ulId,
UINT8 *pucStatus,
ULONG *pulTimeLeft,
LW_HANDLE *pulOwnerId);
ULONG Lw_Rms_GetName(LW_HANDLE ulId, PCHAR pcName);
函数 Lw_Rms_Status 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 ulId 是 RMS 句柄。
- 输出参数 pucStatus 返回 RMS 状态。
- 输出参数 pulTimeLeft 返回等待剩余时间。
- 输出参数 pulOwnerId 返回所有者 ID。
函数 Lw_Rms_GetName 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 ulId 是 RMS 句柄。
- 输出参数 pcName 返回 RMS 名字。
调用 Lw_Rms_Status 函数将返回 RMS 调度器的状态,如果参数 pucStatus 非 NULL,则返回如下表所示状态,如果参数 pulTimeLeft 非 NULL,则返回调度剩余的时间,如果参数 pulOwnerId 非 NULL,则返回 RMS 调度器所有者的线程句柄;调用 Lw_Rms_GetName 函数将返回 RMS 调度器的名字。
状态名 | 说明 |
---|---|
LW_RMS_INACTIVE | RMS 调度器刚创建 |
LW_RMS_ACTIVE | 初始化了周期,测量执行时间 |
LW_RMS_EXPIRED | 有任务阻塞 |
作为 POSIX 的扩展,SylixOS 提供了下面一组函数来实现 POSIX RMS 调度器,相比之前的 RMS 实现,下面的函数更加易用且时间精度更高(纳秒级)。
#include <sched_rms.h>
int sched_rms_init(sched_rms_t *prms, pthread_t thread);
int sched_rms_destroy(sched_rms_t *prms);
int sched_rms_period(sched_rms_t *prms, const struct timespec *period);
函数 sched_rms_init 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 prms 是 RMS 调度器指针。
- 参数 thread 是调用线程的句柄。
函数 sched_rms_destroy 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 prms 是 RMS 调度器指针。
函数 sched_rms_period 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 prms 是 RMS 调度器指针。
- 参数 period 是程序执行周期。
调用 sched_rms_init 函数将初始化参数 prms 指定的 RMS 调度器,与 Lw_Rms_Create 函数不同的是,前者由应用程序创建一个 sched_rms_t 类型的 RMS 调度器,然后调用 sched_rms_init 函数来初始化这个调度器,也就是说,此调度器将由应用程序创建和销毁,而后者创建的 RMS 调度器则由内核管理,也即应用程序不会直接管理所使用的调度器。
调用 sched_rms_destroy 函数将销毁由 sched_rms_init 函数初始化的调度器,被销毁的调度器不能再被使用,除非重新初始化。
调用 sched_rms_period 函数使 RMS 调度器开始工作。
下面程序展示了 RMS 调度器的使用方法。
#include <sched_rms.h>
#include <pthread.h>
sched_rms_t rms;
void process_func(void)
{
int i = 1;
for (; i >= 0; --i) {
sleep(1);
}
}
void *rms_thread (void *arg)
{
struct timespec *period = (struct timespec *)arg;
sched_rms_init(&rms, pthread_self());
while (1) {
if (sched_rms_period(&rms, period) != 0) {
break;
}
process_func();
fprintf(stdout, "rms thread running...\n");
}
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_t tid;
int ret;
struct timespec period;
period.tv_nsec = 0;
period.tv_sec = 3;
ret = pthread_create(&tid, NULL, rms_thread, (void *)&period);
if (ret < 0) {
fprintf(stderr, "pthread_create error.\n");
return (-1);
}
sched_rms_init(&rms, tid);
pthread_join(tid, NULL);
sched_rms_destroy(&rms);
return (0);
}
在 SylixOS Shell 下运行程序:
# ./RMS_Scheduler
rms thread running...
rms thread running...
……
# ts
NAME TID PID PRI STAT LOCK SAFE DELAY PAGEFAILS FPU CPU
---------------- ------- ----- --- ---- ---- ---- ---------- --------- --- ---
RMS_Scheduler 4010033 17 200 JOIN 0 0 1 USE 0
pthread 4010034 17 200 SLP 0 475 0 0
程序设置 RMS 调度周期是 3 秒,线程 rms_thread 的运行函数 process_func 的运行时间为大于 2 秒而小于 3 秒的时间,因此线程能够被正常的调度,程序的运行结果也说明了这一点。如果我们将 process_func 函数中的 i 值改为大于等于 2 的值线程将只被调度一次,因为此时的线程运行时间大于 RMS 调度器的周期值,这将导致调度器发生超时溢出错误(EOVERFLOW)。