Я работаю над vxWorks 7. Я разработал класс для реализации таймера POSIX. Я работаю над процессом в реальном времени.
Таймер предназначен для работы в однократном или периодическом режиме.
Проблема в том, что я получаю случайный тайм-аут. Я приложил список наблюдаемых таймаутов в сравнении с ожидаемыми таймаутами.
Худшая проблема заключается в том, что в Linux (недетерминированная ОС общего назначения) совершенно один и тот же код по сравнению с детерминированной RTOS.
Исходный код :
posix_timer.hpp
#pragma once
#include <map>
#include <ctime>
#include <string>
#include <utility>
#include <csignal>
#include <cstdint>
#include <functional>
#include <chrono>
typedef int signal_t;
static const std::int64_t getCurrentTimeInMicroseconds()
{
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
class PosixTimer
{
public:
static bool initialize(void);
static inline const int signal_rt_min = SIGRTMIN, signal_rt_max = SIGRTMAX;
using callback_timer_t = std::function<void(std::uint64_t)>;
PosixTimer(callback_timer_t callback, std::uint64_t callback_data, bool periodic = false) :
PosixTimer(periodic)
{
this->callback = callback;
this->callback_data = callback_data;
}
PosixTimer(bool periodic);
PosixTimer();
PosixTimer(const PosixTimer& instance) = delete; //Delete copy constructor
PosixTimer& operator=(const PosixTimer& instance) = delete; //Delete Copy Assignment
PosixTimer(PosixTimer&& source);
PosixTimer& operator=(PosixTimer&& source);
~PosixTimer();
bool create(void);//Create timer only
bool create(std::uint64_t timeout, bool periodic);//Initialize the Periodic Timer Object, Create timer, set timeout and start timer
// bool create(std::uint64_t timeout);//Create timer, set timeout and start timer
bool update(std::uint64_t timeout);//Stop timer, update set timeout and start timer
bool start(std::uint64_t timeout);//Set timeout and start timer
bool start(void);//Starts the timer with the timeout value (refer private timeout variables)
bool stop(void);//Stops the timer
bool destroy(void);//Destroys the timer. Can't be reused without calling create
void registerCallback(callback_timer_t callback, std::uint64_t callback_data);
static std::uint64_t in_nanoseconds(std::uint64_t timeout);
static std::uint64_t in_microseconds(std::uint64_t timeout);
static std::uint64_t in_milliseconds(std::uint32_t timeout);
static std::uint64_t in_seconds(std::uint16_t timeout);
private:
static inline std::map<signal_t, bool> signal_usage_status;
signal_t signal_id;
static inline std::uint8_t total_timer_count = 0;
std::uint8_t timer_count = 0;
bool periodic;
std::uint64_t timeout;// in nano-seconds
timer_t timer_id;
sigset_t mask;
std::uint64_t callback_data;
struct sigevent sev;
struct sigaction sa;
struct itimerspec its;
bool timer_valid {false};
static void handler(int sig, siginfo_t *si, void *uc);
callback_timer_t callback;
bool block_signal(void);
bool unblock_signal(void);
};
class SignalNumberExhausted : public std::exception
{
private:
static inline const std::string message = "Real-time signal number exhausted";
public:
SignalNumberExhausted() {}
const char* what() const noexcept override
{
return message.c_str();
}
};
posix_timer.cpp
#include "posix_timer.hpp"
#include <iostream>
#include <cstdio>
// The range of supported real-time signals is defined by the macros SIGRTMIN and SIGRTMAX.
// Programs should never refer to real-time signals using hard-coded numbers, but instead should always refer to
// real-time signals using the notation SIGRTMIN+n, and include suitable (run-time) checks that SIGRTMIN+n
// does not exceed SIGRTMAX
// the top priority RT signal is SIGRTMIN; the least (lowest) priority RT signal is SIGRTMAX.
// If different real-time signals are sent to a process, they are delivered starting with the lowest-numbered signal.
//(i.e., low-numbered signals have highest priority.)
// #define SIG SIGRTMIN
#define CLOCK_ID CLOCK_REALTIME
bool PosixTimer::initialize(void)
{
try
{
for(int i = PosixTimer::signal_rt_min; i <= PosixTimer::signal_rt_max; i++)
{
// std::cout << i << std::endl;
PosixTimer::signal_usage_status[i] = false;
}
return true;
}
catch (const std::exception &e)
{
// Log Error
return false;
}
}
PosixTimer::PosixTimer()
{
std::cout << "Inside constructor PosixTimer::PosixTimer()\n";
}
PosixTimer::PosixTimer(bool periodic)
{
// std::cout << "Inside constructor PosixTimer::PosixTimer(bool periodic)\n";
this->periodic = periodic;
this->timer_count = SIGRTMIN + PosixTimer::total_timer_count++;
// NativePosixTimer::total_timer_count++;
bool signal_assigned = false;
for (auto& [key, value]: PosixTimer::signal_usage_status)
{
if (value == false)//Timer not assigned
{
this->signal_id = key; //Assign the free signal
value = true;
signal_assigned = true;
break;
}
}
if (signal_assigned == false)
{
// std::cout << "In PosixTimer-> If\n";
throw SignalNumberExhausted();
}
}
PosixTimer::PosixTimer(PosixTimer &&source)
{
// std::cout << "Inside Move Constructor PosixTimer::PosixTimer(PosixTimer &&source)\n";
this->timer_id = source.timer_id;
// this->timer_valid = source.timer_valid;
this->signal_id = source.signal_id;
this->timer_count = source.timer_count;
this->periodic = source.periodic;
this->timeout = source.timeout;// in nano-seconds
this->mask = source.mask;
// this->callback_data = source.callback_data;
std::exchange(this->callback_data, source.callback_data);
this->sev = source.sev;
this->sa = source.sa;
this->its = source.its;
this->callback = source.callback;
// this->si
//Reset source timer_id to 0 and timer validity (timer_valid) to false
source.timer_id = 0;
source.timer_valid = false;
if (this->callback)
{
std::cout << "this->callback not empty in move constructor\n";
}
else
{
std::cout << "this->callback empty in move constructor\n";
}
}
PosixTimer &PosixTimer::operator=(PosixTimer &&source)
{
// std::cout << "Inside Move Assignment PosixTimer &PosixTimer::operator=(PosixTimer &&source)\n";
if (this != &source) //performs no operation if you try to assign the object to itself
{
this->timer_id = source.timer_id;
// this->timer_valid = source.timer_valid;
this->signal_id = source.signal_id;
this->timer_count = source.timer_count;
this->periodic = source.periodic;
this->timeout = source.timeout;// in nano-seconds
this->mask = source.mask;
// this->callback_data = source.callback_data;
std::exchange(this->callback_data, source.callback_data);
this->sev = source.sev;
this->sa = source.sa;
this->its = source.its;
this->callback = source.callback;
//Reset source timer_id to 0 and timer validity (timer_valid) to false
source.timer_id = 0;
source.timer_valid = false;
if (this->callback)
{
std::cout << "this->callback not empty in move assignment\n";
}
else
{
std::cout << "this->callback empty in move assignment\n";
}
this->sev.sigev_value.sival_ptr = this;
}
return *this;
}
PosixTimer::~PosixTimer()
{
if (this->timer_valid == true)
{
if (this->destroy() == false)
{
//Log Error
}
}
}
bool PosixTimer::create(void)
{
int returnValue;
this->timer_count = SIGRTMIN + PosixTimer::total_timer_count++;
bool signal_assigned = false;
for (auto& [key, value]: PosixTimer::signal_usage_status)
{
if (value == false)//Timer not assigned
{
this->signal_id = key; //Assign the free signal
value = true;
signal_assigned = true;
break;
}
}
if (signal_assigned == false)
{
// std::cout << "In Create-> If\n";
throw SignalNumberExhausted();
}
//End of Addition by IK on 06-04-2024
// Establish handler for timer signal.
this->sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigemptyset(&this->sa.sa_mask);
if (sigaction(this->signal_id, &this->sa, nullptr) == -1)
{
// Log Error
perror("sigaction in PosixCreate ");
return false;
}
// Block timer signal temporarily.
if (this->block_signal() == false)
{
return false;
}
// Create a timer
this->sev.sigev_notify = SIGEV_SIGNAL; // Upon timer expiration, generate the signal sigev_signo for the process.
this->sev.sigev_signo = this->signal_id;
this->sev.sigev_value.sival_ptr = this;
returnValue = timer_create(CLOCK_ID, &this->sev, &this->timer_id);
if (returnValue == -1)
{
// Log Error
perror("timer_create");
return false;
}
this->timer_valid = true;
return true;
}
bool PosixTimer::create(std::uint64_t timeout, bool periodic)
{
this->periodic = periodic;
bool return_status = this->create();
if (return_status == false)
{
return false;
}
return this->start(timeout);
}
bool PosixTimer::update(std::uint64_t timeout)
{
// //std::cerr<<"TIMER UPDATE CALLED, Timer ID:"<<this->timer_id<<std::endl;
bool return_status = this->stop();
if (return_status == false)
{
return false;
}
return this->start(timeout);
}
bool PosixTimer::start(std::uint64_t timeout)
{
this->its.it_value.tv_sec = timeout / 1000000000;
this->its.it_value.tv_nsec = timeout % 1000000000;
if (periodic == true)
{
this->its.it_interval.tv_sec = this->its.it_value.tv_sec;
this->its.it_interval.tv_nsec = this->its.it_value.tv_nsec;
}
else
{
this->its.it_interval.tv_sec = 0;
this->its.it_interval.tv_nsec = 0;
}
return this->start();
}
bool PosixTimer::start(void)
{
int return_status = timer_settime(this->timer_id, 0, &this->its, nullptr);
if (return_status == -1)
{
perror("PosixTimer::start->timer_settime");
return false;
}
this->unblock_signal();
return true;
}
bool PosixTimer::stop(void)
{
this->block_signal();
struct itimerspec its;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
int return_status = timer_settime(this->timer_id, 0, &its, nullptr);
if (return_status == -1)
{
perror("PosixTimer::stop->timer_settime");
return false;
}
return true;
}
bool PosixTimer::destroy(void)
{
int return_status = timer_delete(this->timer_id);
if (return_status == -1)
{
return false;
}
else
{
this->timer_valid = false;
PosixTimer::signal_usage_status.at(this->signal_id) = false;
return true;
}
}
void PosixTimer::registerCallback(callback_timer_t callback, std::uint64_t callback_data)
{
this->callback_data = callback_data;
this->callback = callback;
}
void PosixTimer::handler(int sig, siginfo_t *si, void *uc)
{
try
{
PosixTimer *timer = reinterpret_cast<PosixTimer *>(si->si_value.sival_ptr);
if (timer->callback)
{
std::cout << "timer->callback is not empty\n";
}
else
{
std::cout << "timer->callback is empty\n";
}
std::cerr<<"Current time in microseconds from posix timer class "<< getCurrentTimeInMicroseconds()<<std::endl;
timer->callback(timer->callback_data);
}
catch(const std::exception& e)
{
std::cerr << "PosixTimer::handler exception. " << e.what() << std::endl;
}
}
bool PosixTimer::block_signal(void)
{
// Block timer signal temporarily.
#ifdef NDEBUG
printf("Blocking signal %d\n", this->signal_id);
#endif
// Empty a signal set
if (sigemptyset(&this->mask) == -1)
{
perror("sigemptyset");
return false;
}
// Add a signal to a signal set
// std::cout << "Signal ID : " << this->signal_id << std::endl;
if (sigaddset(&this->mask, this->signal_id) == -1)
{
perror("sigaddset");
return false;
}
// Change the signal mask of the calling thread
if (sigprocmask(SIG_BLOCK, &this->mask, nullptr) == -1)
{
perror("sigprocmask");
return false;
}
return true;
}
bool PosixTimer::unblock_signal(void)
{
// std::printf("Unblocking signal %d\n", this->signal_id);
if (sigprocmask(SIG_UNBLOCK, &this->mask, NULL) == -1)
{
perror("timer start- sigprocmask");
return false;
}
return true;
}
std::uint64_t PosixTimer::in_milliseconds(std::uint32_t timeout)
{
return timeout * 1000000ul;
}
std::uint64_t PosixTimer::in_nanoseconds(std::uint64_t timeout)
{
return timeout;
}
std::uint64_t PosixTimer::in_microseconds(std::uint64_t timeout)
{
return timeout * 1000ul;
}
std::uint64_t PosixTimer::in_seconds(std::uint16_t timeout)
{
return timeout * 1000000000ul;
}
main.cpp
#include "posix_timer.hpp"
#include <iostream>
#include <vector>
#include <any>
#include <unistd.h>
#include <chrono>
void handler(std::uint64_t data)
{
try
{
std::cout<<"Current time: "<<getCurrentTimeInMicroseconds();
}
catch(const std::exception& e)
{
std::cerr << "Main::handler : " << e.what() << '\n';
}
}
int main(int argc, char const *argv[])
{
PosixTimer::initialize();
PosixTimer t;
t.registerCallback(handler, 10);
bool return_status = t.create(PosixTimer::in_milliseconds(1), true);
if (return_status == true)
{
std::cerr << "Timer Started\n";
}
else
{
std::cerr << "Timer Failed to Start\n";
}
while(1)
{
}
return 0;
}
Время записи:
Всего несколько замечаний: поведение вашего кода неопределенное: while(1){}. Тогда код не является минимально воспроизводимым примером и выходные данные отсутствуют. Наконец, как только все заработает, рассмотрите возможность отправки своего кода на codereview.stackexchange.com.





Судя по документации VXWorks, разрешение часов зависит от оборудования и «во многих случаях составляет 1/60 секунды».
Значения, показанные в таблице, указаны с шагом 1/60 секунды, что соответствует разрешению, указанному в документации. Значения не случайны, а скорее соответствуют ожидаемым.
Если я использую счетчик отметок времени Intel (TSC) — это 64-битный регистр, доступный на процессорах Intel, начиная с Pentium. Столкнусь ли я с той же проблемой? Доступны ли их средства вычислений с координацией времени Intel, которые могут помочь мне решить эту проблему? Я использую процессор Tiger Lake 12-го поколения.
@DarkSorrow Должно быть доступно более точное управление временем, просто документально подтверждено, что выбранный вами конкретный интерфейс (интервальные таймеры POSIX), вероятно, имеет грубые временные интервалы в VxWorks (в тактах, которые обычно составляют 1/60 или 1/100 секунды). ). В имеющейся у меня копии документации VxWorks (в разделе timerLib) упоминается использование nanosleep для интервалов менее секунды (хотя это приостановка, а не обратный вызов).
Можете ли вы настроить тактовую частоту этой сборки VxWorks? Тики действительно являются основной временной разверткой в ОСРВ, все «происходит» с шагом в такты. Еще следует рассмотреть вопрос о том, действительно ли этот дизайн приложения является правильным решением в данной ситуации. Я не уверен, какое программное обеспечение вы пишете, но, как упоминалось в комментариях к вопросу, это много шаблонного кода, просто чтобы обернуть таймер. Какие требования заставляют вас идти по этому пути, но при этом вам нужен очень точный интервальный контроль?
Почему ты путаешь
<ctime>с<chrono>? Еслиstd::chronoдоступен, вам следует использоватьchrono::high_resolution_clockиhigh_resolution_clock::durationв качестве значения тайм-аута. Целые числа нежелательны при наличии определенных типов данных с хорошей абстракцией. Этот код не кажется минималистичным; слишком долго для таймера. Вы не используете список инициализаторов конструктора. Ваш конструктор перемещения и оператор присваивания перемещают почти каждый элемент, который является определением по умолчанию и не требует кодирования; если хотя бы один член пропущен, вы обречены. Отдельный класс может выполнять ход по умолчанию.