timer_create返回timer_id为0且timer_settime报无效参数问题排查
问题
使用C++17标准,启动定时器时出现错误提示:Invalid argument。检查发现timer_create()返回0(表示执行成功),但获取到的timer_id值为0,请问这可能是什么问题?
代码
头文件
#pragma once #include <ctime> #include <csignal> #include <cstdint> #include <functional> //#include <timerLib.h> class Timer { public: Timer(bool periodic = false) : periodic(periodic){} ~Timer(); bool create(void);//Create timer only 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 using callback_timer_t = std::function<void(void)>; void registerCallback(callback_timer_t callback); private: bool periodic; std::uint64_t timeout;// in nano-seconds timer_t timer_id; sigset_t mask; long long freq_nanosecs; struct sigevent sev; struct sigaction sa; struct itimerspec its; static void handler(int sig, siginfo_t *si, void *uc); callback_timer_t callback; };
源文件
#include "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 Timer::~Timer() { this->destroy(); } bool Timer::create(void) { int returnValue; //Establish handler for timer signal. this->sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = handler; sigemptyset(&this->sa.sa_mask); if (sigaction(SIG, &this->sa, nullptr) == -1) { //Log Error perror("sigaction"); return false; } //Block timer signal temporarily. printf("Blocking signal %d\n", SIG); sigemptyset(&mask); sigaddset(&mask, SIG); if (sigprocmask(SIG_SETMASK, &this->mask, nullptr) == -1) { perror("sigprocmask"); 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 = SIG; this->sev.sigev_value.sival_ptr = this; returnValue = timer_create(CLOCK_REALTIME, &this->sev, &this->timer_id); std::cout << "Timer Create Return Value : " << returnValue << std::endl; if(returnValue == -1) { //Log Error perror("timer_create"); return false; } //# -> Used with o, x or X specifiers the value is preceeded with 0, 0x or 0X respectively for values different than zero. //j -> The length sub-specifier modifies the length of the data type : uintmax_t std::printf("timer ID is %#jx\n", (uintmax_t) timer_id); //Only for testing return true; } bool Timer::create(std::uint64_t timeout) { bool return_status = this->create(); if(return_status == false) { return false; } return this->start(timeout); } bool Timer::update(std::uint64_t timeout) { bool return_status = this->stop(); if(return_status == false) { return false; } return this->start(timeout); } bool Timer::start(std::uint64_t timeout) { this->its.it_value.tv_sec = 0; this->its.it_value.tv_nsec = timeout; if(periodic == true) { this->its.it_interval.tv_sec = 0; this->its.it_interval.tv_nsec = timeout; } else { this->its.it_interval.tv_sec = 0; this->its.it_interval.tv_nsec = 0; } return this->start(); } bool Timer::start(void) { int return_status = timer_settime(this->timer_id, 0, &this->its, nullptr); if(return_status == -1) { //TODO Log Error perror("timer_settime"); return false; } //TODO Remove in final code std::cout << "Timer Started\n"; return true; } bool Timer::stop(void) { struct itimerspec its; its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; int return_status = timer_settime(this->timer_id, 0, &its, nullptr); if(return_status == -1) { //TODO Log Error perror("timer_settime"); return false; } //TODO Remove in final code std::cout << "Timer Stopped\n"; return true; } bool Timer::destroy(void) { int return_status = timer_delete(this->timer_id); if(return_status == -1) { return false; } else { return true; } } void Timer::registerCallback(callback_timer_t callback) { this->callback = callback; } void Timer::handler(int sig, siginfo_t *si, void *uc) { Timer *timer = reinterpret_cast<Timer*>(si->si_value.sival_ptr); timer->callback(); }
主函数
#include "timer.hpp" #include <iostream> void timer_callback(void) { std::cout << "Timeout\n"; } int main(int argc, char *argv[]) { Timer t(true); t.registerCallback(timer_callback); if(t.create(1000000000ul) == false) { std::cout << "Timer Create Failed.\tExitting Application.\n"; return -1; } while (true) { /* code */ } return 0; }
输出结果
Blocking signal 34 Timer Create Return Value : 0 timer ID is 0 timer_settime: Invalid argument Timer Create Failed. Exitting Application.
CMakeLists.txt
cmake_minimum_required(VERSION 3.6.2) set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) project(timer_lib C CXX ASM) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -g") SET(MY_TARGET ${PROJECT_NAME}.out) add_executable(${MY_TARGET} main.cpp timer.cpp )
问题分析与解决
1. timer_id为0的合法性
在Linux系统中,timer_t本质是文件描述符,0是合法值(对应标准输入),timer_create返回0说明定时器创建成功,这个值本身没有问题,无需纠结。
2. timer_settime报错的核心原因
你传入的超时时间是1000000000ul(1秒),但代码中直接将其赋值给tv_nsec,而tv_nsec的取值范围是0到999,999,999,超过这个值会触发EINVAL错误(即Invalid argument)。
3. 修复方案
(1)修正超时时间拆分逻辑
修改start(uint64_t timeout)方法,将纳秒数拆分为秒和纳秒两部分:
bool Timer::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(); }
(2)修复信号屏蔽的逻辑错误
在create()方法中,你操作了局部变量mask,但传给sigprocmask的是成员变量this->mask,导致信号屏蔽未正确生效。修改为:
//Block timer signal temporarily. printf("Blocking signal %d\n", SIG); sigemptyset(&this->mask); sigaddset(&this->mask, SIG); if (sigprocmask(SIG_SETMASK, &this->mask, nullptr) == -1)
4. 验证
修改后重新编译运行,timer_settime将不再报错,定时器会正常触发回调。
内容的提问来源于stack exchange,提问作者Dark Sorrow
相关产品推荐
相关产品推荐

