如何安排具有绝对开始时间的实时任务?
How to schedule a real-time task with absolute start time?
对于 TSN 感知通信协议栈和框架,我不仅需要能够以非常精确的间隔 运行 执行实时线程,而且还需要从从 ptpd 中扣除的精确时隙开始。
目前,我通过延迟/放弃任务直到开始时间来做到这一点。对于我的 TSN 通信的 PoC,这已经足够了,但是对于 Linux 实时扩展,必须有更好/更有效的方法。两个 Beaglebones 之间引入的抖动约为 800us(运行 Linux 4.14.71-ti-r80 #1 SMP PREEMPT)。
使用循环 pthread,我同步了网络生产者任务的开始(注意:vos_ 前缀表示我的 OS 抽象方法,实际上匹配 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 的抖动(使用 Linux 4.14 没有 RT-extension 和 policy Round-Robin),这是相当好的 - 但当涉及多个时无效线程都在执行预定的流量...
使用 Linux 4.1.15-ti-rt-r43 #1 SMP PREEMPT RT,我打算使用 SCHED_DEADLINE 作为策略,但结构 sched_attr 没有提供任何开始-时间参数。那么不需要上面代码中的运行时检查。但是:
我怎样才能有效地使用 Linux 的实时扩展在精确的绝对时间有一个线程 运行,例如从绝对实时开始每 1 毫秒 'now' + 0.0075s(例如 now.0085、now.0095、now.0105 ...),没有上面代码的开销?
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 感知通信协议栈和框架,我不仅需要能够以非常精确的间隔 运行 执行实时线程,而且还需要从从 ptpd 中扣除的精确时隙开始。
目前,我通过延迟/放弃任务直到开始时间来做到这一点。对于我的 TSN 通信的 PoC,这已经足够了,但是对于 Linux 实时扩展,必须有更好/更有效的方法。两个 Beaglebones 之间引入的抖动约为 800us(运行 Linux 4.14.71-ti-r80 #1 SMP PREEMPT)。
使用循环 pthread,我同步了网络生产者任务的开始(注意:vos_ 前缀表示我的 OS 抽象方法,实际上匹配 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 的抖动(使用 Linux 4.14 没有 RT-extension 和 policy Round-Robin),这是相当好的 - 但当涉及多个时无效线程都在执行预定的流量...
使用 Linux 4.1.15-ti-rt-r43 #1 SMP PREEMPT RT,我打算使用 SCHED_DEADLINE 作为策略,但结构 sched_attr 没有提供任何开始-时间参数。那么不需要上面代码中的运行时检查。但是:
我怎样才能有效地使用 Linux 的实时扩展在精确的绝对时间有一个线程 运行,例如从绝对实时开始每 1 毫秒 'now' + 0.0075s(例如 now.0085、now.0095、now.0105 ...),没有上面代码的开销?
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 */
}