概念
时间间隔内触发信号或执行回调函数的机制,指定的时间间隔内执行特定的操作。
API函数
timer_create():用于创建一个新的定时器。
timer_settime():用于设置定时器的到期时间和间隔。
timer_gettime():用于获取定时器的当前值。
timer_delete():用于删除指定的定时器。
POSIX定时器的主要结构体:
struct itimerspec:用于表示定时器的时间间隔和初始值。
struct sigevent: 用于指定定时器完成异步IO操作。
int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid);
- clockid:定时器使用的时钟。常见值包括CLOCK_REALTIME用于系统范围的实时时钟和CLOCK_MONOTONIC用于表示单调时间且无法设置的时钟。一般就CLOCK_REALTIME。
- sevp:指向struct sigevent的指针,指定定时器到期时如何通知。如果不需要通知,则可以将其设置为NULL。一般就0和NULL
- timerid:指向timer_t变量的指针,在成功创建计时器时,计时器ID将存储在其中。
int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
- timerid:要设置的定时器的标识符
- flags:用于指定定时器的行为,可以是0或TIMER_ABSTIME。如果设置为TIMER_ABSTIME,则new_value中的时间将被视为绝对时间,否则将被视为相对时间。
- new_value:指向要设置的新的定时器超时时间和间隔时间的结构体指针
- old_value:用于存储旧的定时器超时时间和间隔时间的结构体指针
int timer_gettime(timer_t timerid, struct itimerspec *value);
获取指定定时器的当前计时器值 ,定义一个 struct itimerspec 类型的结构体变量,用于存储获取到的计数值。
int timer_delete(timer_t timerid);
删除一个特定的定时器 。
itimerspec
struct itimerspec {
struct timespec it_interval; // 间隔时间
struct timespec it_value; // 超时时间
};
定时器会在到期后,立即以时间间隔重新开始计时。就是先用超时再用间隔。则间隔时间设为0,就只会执行一次。
sigevent
指定定时器或异步 I/O 操作完成时应该发生的事件,定义通知事件应该如何传递。跟信号一节有关系。这里的异步 I/O是不会阻塞程序的执行,使用创建其他线程执行。
typedef struct sigevent
{
__sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
union
{
int _pad[__SIGEV_PAD_SIZE];
/* When SIGEV_SIGNAL and SIGEV_THREAD_ID set, LWP ID of the
thread to receive the signal. */
__pid_t _tid;
struct
{
void (*_function) (__sigval_t); /* Function to start. */
pthread_attr_t *_attribute; /* Thread attributes. */
} _sigev_thread;
} _sigev_un;
} sigevent_t;
int sigev_notify :该成员指定通知应该如何传递。可能的取值包括:
-SIGEV_SIGNAL :通过触发指定的信号(sigev_signo )来传递通知。
-SIGEV_NONE :不传递通知。
-SIGEV_THREAD::通过调用指定的函数(sigev_notify_function )来传递通知。
2.int sigev_signo :如果sigev_notify设置为SIGEV_SIGNAL,则该成员指定事件发生时要发送的信号编号。
3.union sigval sigev_value :该联合体可以保存信号值或要传递给信号处理程序或线程函数的指针值。
定时器指定函数
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
// 定时器到期时调用的函数
void timer_expired(union sigval sv) {
static int count = 0;
printf("Timer expired %d times\n", ++count);
}
int main() {
struct sigevent sev;
timer_t timerid;
struct itimerspec its;
// 配置定时器事件
sev.sigev_notify = SIGEV_THREAD; // 使用线程通知方式
sev.sigev_notify_function = timer_expired; // 定时器到期时调用的函数
sev.sigev_value.sival_ptr = NULL; // 传递给回调函数的参数,上面的sigval sv便是这个
sev.sigev_notify_attributes = NULL; // 使用默认线程属性,可以省略
// 创建定时器
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create");
exit(EXIT_FAILURE);
}
// 配置定时器时间
its.it_value.tv_sec = 0; // 初始到期时间为0秒
its.it_value.tv_nsec = 100000; // 初始到期时间为100微秒(0.1毫秒)
its.it_interval.tv_sec = 0; // 间隔时间为0秒
its.it_interval.tv_nsec = 100000; // 间隔时间为100微秒(0.1毫秒)
// 启动定时器
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}
// 进入无限循环,保持程序运行
while (1) {
pause(); // 停止程序减少占用
}
return 0;
}
定时器指定信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
// 定时器信号处理函数
void timer_handler(int signum, siginfo_t *si, void *uc)//*si和*uc是信号自带信息
{
static int count = 0;
printf("Timer expired %d times\n", ++count);
}
int main()
{
struct sigaction sa;
struct sigevent sev;
timer_t timerid;
struct itimerspec its;
// 设置信号处理函数
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGRTMIN, &sa, NULL) == -1)
{
perror("sigaction");
exit(EXIT_FAILURE);
}
// 配置定时器事件
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1)
{
perror("timer_create");
exit(EXIT_FAILURE);
}
// 配置定时器时间
its.it_value.tv_sec = 0; // 初始到期时间为0秒
its.it_value.tv_nsec = 100000; // 初始到期时间为100微秒(0.1毫秒)
its.it_interval.tv_sec = 0; // 间隔时间为0秒
its.it_interval.tv_nsec = 100000; // 间隔时间为100微秒(0.1毫秒)
// 启动定时器
if (timer_settime(timerid, 0, &its, NULL) == -1)
{
perror("timer_settime");
exit(EXIT_FAILURE);
}
// 进入无限循环,保持程序运行
while (1)
{
pause(); // 等待信号
}
return 0;
}
可能报错
librt
(实时库)通常包含了一些用于高精度定时器和实时信号的函数。虽然在现代的glibc版本中,许多librt
中的功能已经被整合到主C库libc
中,但在某些情况下,尤其是在较旧的Linux发行版或特定的交叉编译环境中,仍然可能需要显式链接librt
。
报错信息通常会指出某个未定义的引用(undefined reference),在CMakeLists.txt
中通过target_link_libraries(my_program rt)
显式链接。
Linux下定时器扩展
POSIX为软件定时,精度较高。PIT(Programmable Interval Timer) 硬件定时,触发硬中断,这可能导致较大的开销,较为传统。