SylixOS的RMS浅析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SylixOS的RMS浅析相关的知识,希望对你有一定的参考价值。

 

1. 知识简介    1

1.1 嵌入式实时操作系统    1

1.2 优先级调度算法    1

2. 技术实现    2

2.1 函数原型分析    2

2.2 使用流程    2

2.3 源码分析    3

2.4 示例演示    4

3. 参考资料    5

 

 

 

 

  1. 知识简介

  2. 嵌入式实时操作系统

    SylixOS操作系统是实时多任务嵌入式操作系统,所谓实时,是指系统的正确性不仅仅依赖于计算机的逻辑结果而且依赖于结果产生的时间。结果产生的时间就是通常所说的截止期限,描述系统实时性的指标主要有:

  3. 对紧急时间可预见性的快速响应;

  4. 高度的可调度性,所谓调度性是指系统在任务时间需求能够满足下的最高资源利用率,也就是平均每秒的及时执行的任务数量;

  5. 在暂时超负荷情况下的系统稳定性,即当系统超负荷运转导致不能满足所有任务的截止期限需求时,仍然能够保证关键任务的截止期限需求;

    对于实时而言,截止期限的要求是必须得到满足的,但是区分具体应用场合,这种时限要求的严格成都又有所不同。如果这种要求是绝对的,即不满足截止期限的要求计算结果就毫无意义甚至可能造成无法预料的结果或系统致命的错误,那就称之为硬实时系统(Hard Real Time System);SylixOS就是硬实时系统。在硬实时系统中如果出现了这样的情况就意味着巨大的损失和灾难,比如说日本福岛核电站中的堆芯温度控制系统如果没有对堆芯过热做出及时的冷却处理,后果不堪设想。当不满足截止期限的要求时计算机结果的可调度性逐渐减弱但是并不足以造成严重后果,系统仍然继续调度直至任务完成,则称为软实时系统(Soft Real Time System)。软实时系统是指如果在系统负荷较重的时候允许发生错过截止期限的情况而且不会造成太大的危害。硬实时系统和软实时系统的实现区别主要是,在选择调度算法上选择基于优先级调度的算法是以满足软实时系统的需求而且可以提供告诉的响应和大的系统吞吐率,而对硬实时系统来说需要使用的算法就应该是调度方式简单反应速度快的实时调度算法了。

  6. 优先级调度算法

    调度,是内核的主要职责之一,就是确定该轮到那个任务运行了。优先级抢占调度算法中,每个任务根据其重要程度的不同而被赋予一定的优先级。优先级抢占调度算法是指系统运行过程中高优先级的任务可以中断低优先级的任务,让处在就绪态的优先级最高的任务先运行。目前多数实时内核采用优先级可抢占的调度算法,主要原因有以下几点:

    首先,处理异常的任务可能需要抢占正在运行的任务,以便及时对异常做出响应;

    第二,任务的重要性不同,优先级可抢占使一些重要任务有可能抢占正在运行的任务;

    第三,允许任务抢占可得到更为有效的调度;

    根据不同的优先级分配方法,基于优先级的调度算法可以分为如下两种类型:

  7. 静态调度:

    静态调度是在系统开始运行前进行调度的,严格的静态调度在系统运行时无法对任务进行重新调度。静态调度的怒表是把任务分配到各个CPU,并对每一个CPU给出所要运行任务的静态运行顺序。静态调度算法实现简单,调度的额外开销小,在系统超载时可预测性好。但也具有很大的局限性,例如资源利用率低、受系统支持的优先级个数限制以及灵活性和自适应性差等。

    2.动态调度:

    在嵌入式实时系统中,动态调度依赖于任务的优先级。优先级可以静态分配或者依据不同的特征参数,如截止时间、空闲时间或关键性(即任务的重要程度)等进行动态分配。动态调度可以是抢占式的或非抢占式的。当检查到一事件时,动态抢占式算法立即决定是运行与此时间相关的任务,或继续执行当前的任务;对于动态非抢占式算法,它仅仅知道有另一个任务可以运行,在当前任务结束后,它才在就绪的任务中选择一个来运行。

    单调速率调度算法RMSRate Monotonic Scheduling)为每个周期线程指定一个固定不变的优先级,周期最短的线程优先级最高。周期越短,线程的到达频率越高,优先级也越高,这正是此策略被称为速率单调算法的原因。RMS算法也可用于多CPU环境,用于分配任务优先级。这种方法基于哪个任务执行的次数最频繁,执行最频繁的任务优先级最高。

    RMS的由来是从硬实时环境的初始定义开始的:

  8. 所有的任务都是周期性的,各个任务请求的截止期限呈周期性,同时具有恒定的时间间隔;

  9. 所有任务都必须在下一次任务请求到来之前完成;

  10. 所有的任务都是独立的,每一次任务请求不依赖于其他任务的执行或者初始化;

  11. 每个任务都存在一个恒定且非时变的执行时间,即CPU不间断地执行该任务的时间;

  12. 任何非周期的任务属于特殊任务,它们属于初始化或者是恢复错误的时间,仅当它们自身执行的时候才恩能够取代周期性任务,同时非周期性任务不存在有截止期限。

  13. 技术实现

  14. 函数原型分析

    #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 是程序执行周期。

  15. 使用流程

    SylixOSRMS调度器使用流程如图 2-1所示。

    技术分享

     2-1RMS调度器使用流程图

  16. 源码分析

  17. 初始化调度器时,如图 2-2所示调用API_ThreadSetSchedParam()函数设置线程调度参数,首先将线程调度策略设置为FIFO(先入先出);然后将该线程设置为LW_OPTION_RESPOND_IMMIEDIA(高速响应线程),该线程就绪后会加入到就绪环头部。(就绪环概念请参考《SylixOS线程调度浅析》文档)

     

     if (API_ThreadSetSchedParam(threadLW_OPTION_SCHED_FIFO,

    LW_OPTION_RESPOND_IMMIEDIA)) {

    errno = ESRCH;

    return (PX_ERROR);

    }

     2-2设置线程参数

  18. 启动调度器时,如图 2-3所示,第一次执行时间是不准确的,首先使用lib_clock_gettime()函数获得第一次启动时时间。等第二次执行时依然使用lib_clock_gettime()函数获得第二次启动时间,计算两次执行时间间隔;然后通过__timespecSub()函数计算时间间隔与所定周期的时间差;然后调用__timespecAdd()函数使程序以确定周期运行;这时可以确定程序运行与所定周期的时间差,最后调用nanosleep()函数使该线程睡眠此时间差时间。这样就能保证调度器自动调整时间。

    lib_clock_gettime(CLOCK_MONOTONIC, &temp); /* 获得当前时间 */

    etime = temp;

     

    __timespecSub(&etime, &prms->PRMS_tsSave);

    if (__timespecLeftTime(period, &etime)) { /* 执行时间超过周期 */

    lib_clock_gettime(CLOCK_MONOTONIC, &prms->PRMS_tsSave); /* 获得当前时间 */

    errno = EOVERFLOW;

    return (PX_ERROR);

    }

     

    temp = *period;

    __timespecSub(&temp, &etime);

     

    /*

    * 注意: 这里直接加上周期是为了让每次测算都是以一个固定周期律进行

    * 提高周期精度. (不使用 lib_clock_gettime())

    */

    __timespecAdd(&prms->PRMS_tsSaveperiod); /* 以确定周期运行 */

    return (nanosleep(&tempLW_NULL));

     2-3启动调度器过程

  19. 调度器销毁时直接将RMS结构体sched_rms_t成员PRMS_iStatus0

  20. 示例演示

    如图 2-4 所示为RMS使用用例,设置RMS调度器时间为3秒,代码执行时间随机产生,最终测试结果为3秒打印一次。

    #include <stdio.h>

    #include <sched_rms.h>

    #include <pthread.h>

    #include <stdlib.h>

    #include <time.h>

     

    sched_rms_t rms;

    void process_func(void/* 执行函数 */

    {

    srand(time(NULL)); /* 产生随机数 */

    int i = rand()%2;

     

    for (; i >= 0; --i) { /* 随机睡眠一段时间 */

    sleep(1);

    }

    }

    void *rms_thread ()

    {

    pthread_t tid;

    struct timespec period/* 设置调度周期为3秒 */

    period.tv_nsec = 0;

    period.tv_sec = 3;

     

    tid = pthread_self(); /* 获得自身tid */

    sched_rms_init(&rmstid); /* 初始化RMS调度器 */

    while (1) {

    if (sched_rms_period(&rms, &period) != 0) { /* 启动RMS调度器 */

    break;

    }

     

    fprintf(stdout"rms thread running...\n");

    process_func();

    }

    return (NULL);

    }

    int main (int argcchar *argv[])

    {

    pthread_t tid;

    int ret;

     

    ret = pthread_create(&tidNULLrms_threadNULL); /* 创建线程 */

    if (ret < 0) {

    fprintf(stderr"pthread_create error.\n");

    return (-1);

    }

    pthread_join(tidNULL);

    sched_rms_destroy(&rms); /* 销毁RMS调度器 */

    return (0);

    }

     2-4 使用用例


以上是关于SylixOS的RMS浅析的主要内容,如果未能解决你的问题,请参考以下文章

SylixOS线程堆栈大小浅析

SylixOS的信号屏蔽浅析

SylixOS钩子函数浅析

SylixOS线程私有数据浅析

SylixOS 用户进程加载浅析

基于SylixOS的中断浅析