Для моего приложения у меня есть требование точных периодических потоков с относительно малым временем цикла (500 мкс).
В частности, приложение представляет собой систему времени выполнения
PLC .
Его цель - запустить приложение, разработанное пользователем ПЛК.
Такие приложения организованы в программы и периодические задачи - каждая задача имеет свое время цикла и приоритет.
Обычно приложение работает в системах с операционными системами реального времени (например, vxWorks или Linux с исправлением RT).
В настоящее время периодические задачи реализуются через clock_nanosleep
.
К сожалению, фактическое время сна clock_nanosleep
нарушается другими потоками - даже с более низким приоритетом.
Раз в секунду время ожидания превышается примерно на 50 мс.
Я наблюдал это на Debian 9.5, на RaspberryPi, а также на ARM-Linux с Preemt-RT.
Вот пример, который показывает это поведение:
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
typedef void* ThreadFun(void* param);
#define SCHEDULER_POLICY SCHED_FIFO
#define CLOCK CLOCK_MONOTONIC
#define INTERVAL_NS (10 * 1000 * 1000)
static long tickCnt = 0;
static long calcTimeDiff(struct timespec const* t1, struct timespec const* t2)
{
long diff = t1->tv_nsec - t2->tv_nsec;
diff += 1000000000 * (t1->tv_sec - t2->tv_sec);
return diff;
}
static void updateWakeTime(struct timespec* time)
{
uint64_t nanoSec = time->tv_nsec;
struct timespec currentTime;
clock_gettime(CLOCK, ¤tTime);
while (calcTimeDiff(time, ¤tTime) <= 0)
{
nanoSec = time->tv_nsec;
nanoSec += INTERVAL_NS;
time->tv_nsec = nanoSec % 1000000000;
time->tv_sec += nanoSec / 1000000000;
}
}
static void* tickThread(void *param)
{
struct timespec sleepStart;
struct timespec currentTime;
struct timespec wakeTime;
long sleepTime;
long wakeDelay;
clock_gettime(CLOCK, &wakeTime);
wakeTime.tv_sec += 2;
wakeTime.tv_nsec = 0;
while (1)
{
clock_gettime(CLOCK, &sleepStart);
clock_nanosleep(CLOCK, TIMER_ABSTIME, &wakeTime, NULL);
clock_gettime(CLOCK, ¤tTime);
sleepTime = calcTimeDiff(¤tTime, &sleepStart);
wakeDelay = calcTimeDiff(¤tTime, &wakeTime);
if (wakeDelay > INTERVAL_NS)
{
printf("sleep req=%-ld.%-ld start=%-ld.%-ld curr=%-ld.%-ld sleep=%-ld delay=%-ld\n",
(long) wakeTime.tv_sec, (long) wakeTime.tv_nsec,
(long) sleepStart.tv_sec, (long) sleepStart.tv_nsec,
(long) currentTime.tv_sec, (long) currentTime.tv_nsec,
sleepTime, wakeDelay);
}
tickCnt += 1;
updateWakeTime(&wakeTime);
}
}
static void* workerThread(void *param)
{
while (1)
{
}
}
static int createThread(char const* funcName, ThreadFun* func, int prio)
{
pthread_t tid = 0;
pthread_attr_t threadAttr;
struct sched_param schedParam;
printf("thread create func=%s prio=%d\n", funcName, prio);
pthread_attr_init(&threadAttr);
pthread_attr_setschedpolicy(&threadAttr, SCHEDULER_POLICY);
pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED);
schedParam.sched_priority = prio;
pthread_attr_setschedparam(&threadAttr, &schedParam);
if (pthread_create(&tid, &threadAttr, func, NULL) != 0)
{
return -1;
}
printf("thread created func=%s prio=%d\n", funcName, prio);
return 0;
}
#define CREATE_THREAD(func,prio) createThread(#func,func,prio)
int main(int argc, char*argv[])
{
int minPrio = sched_get_priority_min(SCHEDULER_POLICY);
int maxPrio = sched_get_priority_max(SCHEDULER_POLICY);
int prioRange = maxPrio - minPrio;
CREATE_THREAD(tickThread, maxPrio);
CREATE_THREAD(workerThread, minPrio + prioRange / 4);
sleep(10);
printf("%ld ticks\n", tickCnt);
}
Что-то не так в моем примере кода?
Есть ли лучший (более надежный) способ создания периодических потоков?