Linux 中用户态C++定时器的使用
在Linux中,timer_create
函数可以使用不同的时钟选项来创建定时器。主要的时钟选项有以下几种:
- CLOCK_REALTIME
- CLOCK_MONOTONIC
- CLOCK_PROCESS_CPUTIME_ID
- CLOCK_THREAD_CPUTIME_ID
- CLOCK_BOOTTIME
- CLOCK_REALTIME_ALARM
- CLOCK_BOOTTIME_ALARM
1. CLOCK_REALTIME
- 用途:基于系统实时时钟。这个时钟可以被系统时间的改变所影响(例如通过
settimeofday
或NTP同步)。 - 用法:通常用于需要知道真实时间的应用。
timer_create(CLOCK_REALTIME, &sev, &timerId);
2. CLOCK_MONOTONIC
- 用途:单调递增时钟。这个时钟不会受到系统时间改变的影响,适合测量时间间隔。
- 用法:适用于需要稳定时间间隔的应用,如测量程序运行时间。
timer_create(CLOCK_MONOTONIC, &sev, &timerId);
3. CLOCK_PROCESS_CPUTIME_ID
- 用途:用于测量调用进程的CPU时间。
- 用法:适合用于需要精确测量某个进程CPU使用时间的应用。
timer_create(CLOCK_PROCESS_CPUTIME_ID, &sev, &timerId);
4. CLOCK_THREAD_CPUTIME_ID
- 用途:用于测量调用线程的CPU时间。
- 用法:适合用于需要精确测量某个线程CPU使用时间的应用。
timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timerId);
5. CLOCK_BOOTTIME
- 用途:类似于
CLOCK_MONOTONIC
,但包括系统睡眠时间。 - 用法:适合需要知道系统运行总时间(包括睡眠时间)的应用。
timer_create(CLOCK_BOOTTIME, &sev, &timerId);
6. CLOCK_REALTIME_ALARM
- 用途:类似于
CLOCK_REALTIME
,但用于唤醒系统或发出警报。 - 用法:适合用于需要在特定时间唤醒系统的应用。
timer_create(CLOCK_REALTIME_ALARM, &sev, &timerId);
7. CLOCK_BOOTTIME_ALARM
- 用途:类似于
CLOCK_BOOTTIME
,但用于唤醒系统或发出警报。 - 用法:适合用于需要在特定时间唤醒系统且包括睡眠时间的应用。
timer_create(CLOCK_BOOTTIME_ALARM, &sev, &timerId);
示例代码
以下是使用CLOCK_MONOTONIC
时钟选项的示例代码:
#include <iostream>
#include <signal.h>
#include <time.h>
#include <unistd.h>
// 定时器触发的处理函数
void timerHandler(int sig, siginfo_t *si, void *uc) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
std::cout << "Timer triggered! Current time: " << ts.tv_sec << " seconds and " << ts.tv_nsec << " nanoseconds" << std::endl;
}
int main() {
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerHandler;
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL);
timer_t timerId;
struct sigevent sev;
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerId;
timer_create(CLOCK_MONOTONIC, &sev, &timerId);
struct itimerspec its;
its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 1;
its.it_interval.tv_nsec = 0;
timer_settime(timerId, 0, &its, NULL);
sleep(5); // 主线程休眠5秒
timer_delete(timerId); // 停止定时器
return 0;
}
定时器不销毁但cancel的方法
下面详细介绍如何创建定时器,并通过将 itimerspec
结构的所有字段设置为零来取消定时器。
注意
如果老设置的struct itimerspec
的it_interval
被设置非零0后会设置cancel失败;
timer_settime
启动或停止一个定时器。
#include <time.h>
int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
itimerspec
描述定时器的启动时间和间隔时间。
struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};
示例代码
下面是一个完整的示例代码,展示了如何创建一个定时器并取消它。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
void timer_handler(int sig, siginfo_t *si, void *uc) {
printf("Timer expired!\n");
}
int main() {
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
struct sigaction sa;
// 设置定时器的处理函数
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigaction(SIGRTMIN, &sa, NULL);
// 创建定时器
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 = 2; // 2秒后触发定时器
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0; // 不重复
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}
// 等待3秒,让定时器触发
sleep(3);
// 取消定时器
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime (cancel)");
exit(EXIT_FAILURE);
}
// 等待2秒,验证定时器已取消
sleep(2);
printf("Timer cancelled.\n");
// 删除定时器
if (timer_delete(timerid) == -1) {
perror("timer_delete");
exit(EXIT_FAILURE);
}
return 0;
}
代码解析
- 设置信号处理函数:使用
sigaction
设置定时器到期时的处理函数timer_handler
。 - 创建定时器:使用
timer_create
创建一个新的定时器。 - 设置定时器时间:使用
timer_settime
设置定时器的初始到期时间和间隔时间。 - 等待定时器触发:通过
sleep
等待定时器触发。 - 取消定时器:再次使用
timer_settime
,将itimerspec
结构的所有字段设置为零,取消定时器。 - 验证定时器已取消:通过
sleep
验证定时器已取消,不会再触发。 - 删除定时器:使用
timer_delete
删除定时器。
回调函数关联上下文的用法
可以通过设置 sigevent 结构体中的 sigev_value 字段,将上下文信息传递给 timer_handler 回调函数。sigev_value 字段可以是一个 union,它可以存储指向任意数据类型的指针。这样,当定时器到期时,信号处理函数可以访问这些上下文信息。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
typedef struct {
timer_t timerid;
int some_data;
} timer_context_t;
void timer_handler(int sig, siginfo_t *si, void *uc) {
timer_context_t *ctx = (timer_context_t *)si->si_value.sival_ptr;
printf("Timer expired! some_data = %d\n", ctx->some_data);
}
int main() {
timer_context_t ctx;
struct sigevent sev;
struct itimerspec its;
struct sigaction sa;
// 设置定时器的处理函数
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigaction(SIGRTMIN, &sa, NULL);
// 创建定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &ctx;
if (timer_create(CLOCK_REALTIME, &sev, &ctx.timerid) == -1) {
perror("timer_create");
exit(EXIT_FAILURE);
}
// 设置上下文数据
ctx.some_data = 42;
// 设置定时器初始时间和间隔时间
its.it_value.tv_sec = 2; // 2秒后触发定时器
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0; // 不重复
its.it_interval.tv_nsec = 0;
if (timer_settime(ctx.timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}
// 等待3秒,让定时器触发
sleep(3);
// 取消定时器
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(ctx.timerid, 0, &its, NULL) == -1) {
perror("timer_settime (cancel)");
exit(EXIT_FAILURE);
}
// 等待2秒,验证定时器已取消
sleep(2);
printf("Timer cancelled.\n");
// 删除定时器
if (timer_delete(ctx.timerid) == -1) {
perror("timer_delete");
exit(EXIT_FAILURE);
}
return 0;
}
代码解析
- 设置信号处理函数:使用
sigaction
设置定时器到期时的处理函数timer_handler
。 - 创建定时器:使用
timer_create
创建一个新的定时器,并将上下文数据ctx
通过sigev_value.sival_ptr
传递。 - 设置上下文数据:在上下文结构体中存储需要在处理函数中访问的数据。
- 设置定时器时间:使用
timer_settime
设置定时器的初始到期时间和间隔时间。 - 等待定时器触发:通过
sleep
等待定时器触发,并在触发时通过上下文数据进行处理。 - 取消定时器:再次使用
timer_settime
,将itimerspec
结构的所有字段设置为零,取消定时器。 - 验证定时器已取消:通过
sleep
验证定时器已取消,不会再触发。 - 删除定时器:使用
timer_delete
删除定时器。