通过定时器来体会实时系统的实时性

发布时间:2024年01月20日

做嵌入式开发都听过或使用过RTOS,其名字中的RT是Real Time的缩写,意思是如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。

但是… 道理都懂,但是没亲眼看到就无法体会这个实时的效果。本文使用桌面端Linux来讲述一个反例,即非实时操作系统的表现,通过这个例子来反向感受实时系统的实时性。


一 原理

思路是这样的:

  • 准备一个50ms定时器,当定时时间到了之后去做一些事情,同时记录一下当前的时间,假设为t1
  • 等下一个周期到了再记录一下时间,假设为t2,然后计算t2和t1之间的时间差,按照理想预期,这个时间差应该是50ms左右

下面是本人画的原理图,
在这里插入图片描述


二 代码例子

下面是C代码,运行在Linux系统下,本人使用的是debian10,

#include <sys/timerfd.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>


#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

const unsigned long CONVERTER = 1000 * 1000; // 1s == 1000 * 1000 us


void dummyFunc()
{
    for(uint32_t i = 0; i < 1000; i++);
}

int main(void)
{
    int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    if (timerfd == -1)
    {
        handle_error("timerfd_create");
    }

    struct itimerspec new_value = {};
    new_value.it_value.tv_sec  = 0;
    new_value.it_value.tv_nsec = 50*1000*1000; // 50 ms

    new_value.it_interval.tv_sec  = 0;
    new_value.it_interval.tv_nsec = 50*1000*1000; // 50 ms

    if (timerfd_settime(timerfd, 0, &new_value, NULL) == -1)
    {
        handle_error("timerfd_settime");
    }

    struct timeval t1;
    struct timeval t2;
    int flag = 0;


    uint64_t exp = 0;
    while (1)
    {
        int ret = read(timerfd, &exp, sizeof(uint64_t));
        if (ret == sizeof(uint64_t)) // 定时时间到了
        {
            // printf("ret: %d\n", ret);
            if (flag == 0)
            {
                ret = gettimeofday(&t1, NULL);
                if (ret == -1)
                {
                    printf("Error: gettimeofday() on t1\n");
                    return ret;
                }

                flag = 1;
            }
            else
            {
                ret = gettimeofday(&t2, NULL);
                if (ret == -1)
                {
                    printf("Error: gettimeofday() on t2\n");
                    return ret;
                }

                unsigned long diff = (t2.tv_sec * CONVERTER + t2.tv_usec) - (t1.tv_sec * CONVERTER + t1.tv_usec);
                if (diff > 53000 || diff < 47000) // range is [-3ms, +3ms]
                {
                    printf("-----> diff: %u\n", diff);
                }
                
            
                flag = 0;
            }


            dummyFunc();

        }
    }

    return 0;
}

代码里设置为当时间差在50ms±3ms范围之外才才打印,也就是小于47ms或者大于53ms;dummyFunc()就是一个简单循环,耗时都是纳秒级别的。

最后编译并执行,等待一段时间,出现如下,
在这里插入图片描述

可以看到出现了多次时间差不在设定范围内的情况,那么也就可以推断出dummyFunc()并不是每次都能每隔50ms执行一次,有时会出现超过3ms的延时。

为什么会这样呢?因为所使用的的Linux系统是非实时的,线程执行时会被调度,当走到线程某个点前,可能已经调度过多次了,这样就出现了delay。

通过ps -A来查看当前程序的id,然后使用chrt来查看该线程的调度策略,
在这里插入图片描述

这里是SCHED_OTHER,是系统默认的调度策略。

如果系统是实时的,并把线程优先级设置为较高级别,那么这个时间差就是50ms左右了,当然不是正好50ms,会有点偏差,但是偏差会很小,比非实时系统要好很多。


三 总结

本文通过非实时系统的定时器来反向体会实时系统的实时特性。

在现实生活中也是根据需要来选择实时还是非实时:如果任务比较单一,那就用实时;如果要运行多个任务,且每个任务优先级都差不多,那使用非实时体验会好点,那点延时基本感觉不到,如PC电脑,手机等。

文章来源:https://blog.csdn.net/whahu1989/article/details/135714543
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。