如何安排具有绝对开始时间的实时任务?

Posted

技术标签:

【中文标题】如何安排具有绝对开始时间的实时任务?【英文标题】:How to schedule a real-time task with absolute start time? 【发布时间】:2019-01-17 17:15:50 【问题描述】:

对于 TSN 感知的通信协议栈和框架,我需要能够执行实时线程,不仅以非常精确的间隔运行,而且从 ptpd 中扣除的精确时隙开始。

目前,我通过延迟/让出任务直到开始时间来做到这一点。对于我的 TSN 通信的 PoC,这已经足够了,但是对于 Linux 实时扩展,必须有更好/更有效的方法。引入的抖动在两个 Beaglebone 之间约为 800us(运行 Linux 4.14.71-ti-r80 #1 SMP PREEMPT)。

使用循环 pthread,我同步了我的网络生产者任务的启动(注意:vos_ 前缀表示我的操作系统抽象方法,实际上匹配 POSIX 调用):

for (;; )

    /* Synchronize with starttime */
    vos_getTime(&now);                      /* get initial time */
    vos_subTime(&now, &startTime);

    /* Wait for multiples of interval */

    execTime = ((UINT32)now.tv_usec % interval);
    waitingTime = interval - execTime;
    if (waitingTime >= interval)
    
        ...error checks omitted
    

    /* Idle for the difference */
    vos_threadDelay(waitingTime); /* pthread_testcancel() inside */

    vos_getTime(&priorCall);  /* get initial time */
    pFunction(pArguments);    /* thread function: send data */
    vos_getTime(&afterCall);  /* get time after function has returned */

    /* get the runtime of pFunction() */
    vos_subTime(&afterCall, &priorCall);

    /* afterCall holds now the time difference... */
    if (afterCall.tv_sec <= MAXSEC_FOR_USECPRESENTATION)
    
        ...runtime / error handling omitted
    

上面的代码在 5ms 的时间间隔内产生了大约 800us 的抖动(使用没有 RT 扩展和策略 Round-Robin 的 Linux 4.14),这是相当好的 - 但当涉及到多个线程都执行预定流量时无效。 ..

对于 Linux 4.1.15-ti-rt-r43 #1 SMP PREEMPT RT,我计划使用 SCHED_DEADLINE 作为策略,但 struct sched_attr 不提供启动时间参数。那么就不需要上面代码中的运行时检查了。但是:

我怎样才能有效地使用 Linux 的实时扩展来让线程在精确的绝对时间运行,例如从绝对实时“现在”+ 0.0075 秒开始每 1 毫秒(例如 now.0085、now.0095、 now.0105 ...),没有上述代码的开销?

【问题讨论】:

【参考方案1】:

clock_nanosleep()POSIX 函数有一个绝对期限模式:

#define NSEC_PER_SEC 1000000000
/* Initial delay, 7.5ms */
const long start_delay_ns = 7500000;
/* Cycle time, 1ms */
const long cycle_time_ns = 1000000;
struct timespec deadline;

clock_gettime(CLOCK_MONOTONIC, &deadline);
deadline.tv_nsec += start_delay_ns;
deadline.tv_sec += deadline.tv_nsec / NSEC_PER_SEC;
deadline.tv_nsec %= NSEC_PER_SEC;

for (;;)

    struct timespec now;

    /* Sleep until deadline */
    while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
        if (errno != EINTR)
            return; /* error handling here */

    pFunction(pArguments);    /* thread function: send data */

    /* Calculate next deadline */
    deadline.tv_nsec += cycle_time_ns;
    deadline.tv_sec += deadline.tv_nsec / NSEC_PER_SEC;
    deadline.tv_nsec %= NSEC_PER_SEC;

    clock_gettime(CLOCK_MONOTONIC, &now);
    if (now.tv_sec > deadline.tv_sec || (now.tv_sec == deadline.tv_sec && deadline.tv_nsec > now.tv_nsec))
        return; /* time overrun error handling here */

【讨论】:

谢谢,咖啡馆。对于 TSN,我需要 CLOCK_REALTIME,因为它是由 PTP 守护程序调整的。在我的代码中,vos_threadDelay 也使用了 clock_nanosleep()。 @Bernd: CLOCK_MONOTONIC 也可以通过时钟频率调整来调整,尽管它不受时钟跳跃的影响。您的 vos_threadDelay 正在使用时钟的相对超时,而通过使用绝对超时(使用 TIMER_ABSTIME 标志请求)更准确地完成休眠直到截止日期。

以上是关于如何安排具有绝对开始时间的实时任务?的主要内容,如果未能解决你的问题,请参考以下文章

Linux的实时任务调度

任务安排一

[Luogu2365]任务安排(斜率优化)

NOIP模拟 Problem 1 任务安排 题解

bzoj 2726 任务安排

bzoj 2726: [SDOI2012]任务安排