linux定时器操作
目录
- 1 简单示例
- 2 timer_create方式
- 2.1 SIGEV_SIGNAL信号方式通知
- 2.2 SIGEV_THREAD启动线程方式通知
- 2.3 参数
1 简单示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
void setup_timer(int seconds) {
struct itimerval new_value, old_value;
// 设置定时器初始值
new_value.it_value.tv_sec = seconds;
new_value.it_value.tv_usec = 0;
// 设置定时器触发周期,0表示不重复
new_value.it_interval.tv_sec = 0;
new_value.it_interval.tv_usec = 0;
// 设置定时器类型为ITIMER_REAL
setitimer(ITIMER_REAL, &new_value, &old_value);
}
void signal_handler(int sig) {
if (sig == SIGALRM) {
printf("Timer expired\n");
}
}
int main() {
// 设置信号处理函数
signal(SIGALRM, signal_handler);
// 设置定时器,每1秒触发一次
setup_timer(1);
// 执行其他任务
while(1) {
sleep(1);
}
return 0;
}
2 timer_create方式
2.1 SIGEV_SIGNAL信号方式通知
当需要在定时器到期时触发一个信号,并且希望在信号处理器中执行特定的操作时使用。这是最常见的用法之一,适用于需要实时响应的应用程序
- 创建信号捕捉器
sigaction
- 创建定时器
timer_create
- 启动定时器
timer_settimer
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 信号处理函数
void signal_handler(int signum, siginfo_t *info, void *context) {
if (signum == SIGUSR1) {
//使用设置的info->si_value.sival_ptr来判断定时器编号
printf("Timer 1 expired, data: %p\n", info->si_value.sival_ptr);
}
}
int main() {
struct sigevent sev1, sev2;
timer_t timerid1, timerid2;
// TODO 设置信号处理器
struct sigaction sa;
sa.sa_flags = SA_SIGINFO; // 使用 sa_sigaction 而不是 sa_handler
sa.sa_sigaction = signal_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1 || sigaction(SIGUSR2, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
// TODO 配置第一个定时器
sev1.sigev_notify = SIGEV_SIGNAL;
sev1.sigev_signo = SIGUSR1;
sev1.sigev_value.sival_ptr = (void*) "Timer 1";
if (timer_create(CLOCK_REALTIME, &sev1, &timerid1) == -1) {
perror("timer_create for timer 1");
exit(EXIT_FAILURE);
}
// TODO 配置第二个定时器
sev2.sigev_notify = SIGEV_SIGNAL;
sev2.sigev_signo = SIGUSR1;
sev2.sigev_value.sival_ptr = (void*) "Timer 2";
if (timer_create(CLOCK_REALTIME, &sev2, &timerid2) == -1) {
perror("timer_create for timer 2");
exit(EXIT_FAILURE);
}
// 设置定时器1到期时间
struct itimerspec its1;
its1.it_value.tv_sec = 5;
its1.it_value.tv_nsec = 0;
its1.it_interval.tv_sec = 0;
its1.it_interval.tv_nsec = 0;
if (timer_settime(timerid1, 0, &its1, NULL) == -1) {
perror("timer_settime for timer 1");
exit(EXIT_FAILURE);
}
// 设置定时器2到期时间
struct itimerspec its2;
its2.it_value.tv_sec = 10;
its2.it_value.tv_nsec = 0;
its2.it_interval.tv_sec = 0;
its2.it_interval.tv_nsec = 0;
if (timer_settime(timerid2, 0, &its2, NULL) == -1) {
perror("timer_settime for timer 2");
exit(EXIT_FAILURE);
}
// 保持程序运行
while (1) {
pause();
}
return 0;
}
2.2 SIGEV_THREAD启动线程方式通知
当需要在一个新的线程中执行某个函数来处理定时器事件时使用。这种方式提供了更多的灵活性,因为你可以执行任意复杂的任务,而不受信号处理函数的限制。
- 创建线程函数
- 创建定时器
timer_create
- 启动定时器
timer_settimer
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// 线程函数
void* timer_thread_function(union sigval sv) {
printf("Timer thread started, data: %p\n", sv.sival_ptr);
// 在这里执行你的定时器任务
sleep(2);
return NULL;
}
int main() {
struct sigevent sev1, sev2;
timer_t timerid1, timerid2;
// 配置第一个定时器
sev1.sigev_notify = SIGEV_THREAD;
sev1.sigev_notify_function = timer_thread_function;
sev1.sigev_value.sival_ptr = (void*) "Timer 1";
if (timer_create(CLOCK_REALTIME, &sev1, &timerid1) == -1) {
perror("timer_create for timer 1");
exit(EXIT_FAILURE);
}
// 配置第二个定时器
sev2.sigev_notify = SIGEV_THREAD;
sev2.sigev_notify_function = timer_thread_function;
sev2.sigev_value.sival_ptr = (void*) "Timer 2";
if (timer_create(CLOCK_REALTIME, &sev2, &timerid2) == -1) {
perror("timer_create for timer 2");
exit(EXIT_FAILURE);
}
// 设置定时器到期时间
struct itimerspec its1, its2;
// 定时器1:5秒后到期
its1.it_value.tv_sec = 5;
its1.it_value.tv_nsec = 0;
its1.it_interval.tv_sec = 0;
its1.it_interval.tv_nsec = 0;
if (timer_settime(timerid1, 0, &its1, NULL) == -1) {
perror("timer_settime for timer 1");
exit(EXIT_FAILURE);
}
// 定时器2:10秒后到期
its2.it_value.tv_sec = 10;
its2.it_value.tv_nsec = 0;
its2.it_interval.tv_sec = 0;
its2.it_interval.tv_nsec = 0;
if (timer_settime(timerid2, 0, &its2, NULL) == -1) {
perror("timer_settime for timer 2");
exit(EXIT_FAILURE);
}
// 保持程序运行
while (1) {
pause();
}
return 0;
}
注意事项
- 线程资源管理: 每次定时器到期都会创建一个新的线程。如果定时器频繁到期,可能会导致大量的线程被创建,从而消耗大量系统资源。在这种情况下,可以考虑使用线程池或其他资源管理机制来优化线程的创建和销毁
- 线程安全: 如果线程函数需要访问共享资源,确保这些操作是线程安全的,可能需要使用互斥锁或其他同步机制。
2.3 参数
sigevent
结构体用于指定当定时器到期或其他事件发生时,系统应该如何通知应用程序。sigevent.sigev_notify
成员可以设置为不同的值,以选择不同的通知方式。常见的几种方式包括:
SIGEV_NONE
说明:不进行任何通知。
使用场景:当你不需要在定时器到期时收到任何通知时使用。例如,你可能只是想让定时器运行并记 录某些内部状态,而不需要立即处理。
SIGEV_SIGNAL:
说明:通过发送信号来通知。
使用场景:当需要在定时器到期时触发一个信号,并且希望在信号处理器中执行特定的操作时使用。这是最常见的用法之一,适用于需要实时响应的应用程序。
配置:需要设置 sigev_signo 为要发送的信号编号,还可以设置 sigev_value 传递额外的数据给信号处理器。
SIGEV_THREAD:
说明:通过创建一个新的线程来通知。
使用场景:当需要在一个新的线程中执行某个函数来处理定时器事件时使用。这种方式提供了更多的灵活性,因为你可以执行任意复杂的任务,而不受信号处理函数的限制。
配置:需要设置 sigev_notify_function 为要调用的函数指针,sigev_notify_attributes 可以设置线程属性(可选),sigev_value 可以传递参数给该函数。
SIGEV_THREAD_ID (POSIX.1-2008 及更高版本):
说明:通过向指定的线程发送信号来通知。
使用场景:当需要将信号发送到特定的线程而不是整个进程时使用。这种方式允许更细粒度的控制,特别是在多线程环境中。
配置:需要设置 sigev_notify_thread_id 为目标线程的线程ID,sigev_signo 为要发送的信号编号,sigev_value 可以传递额外的数据。