文章

Linux Posix定时器使用

概要

Linux 中有两种设置定时器的方法:内核接口和 Posix 接口。本文介绍 Posix 定时器的使用,内核接口参考Linux 内核学习笔记之定时器和时间管理

配置定时器模式

线程模式

线程模式就是每次定时器触发时都开启一个线程执行任务,优点是使用比较方便,缺点就是每次都会创建一个线程,如果定时器触发比较频繁就会产生大量的线程,性能也会受影响。

1
2
3
4
5
6
7
8
9
10
#include <time.h>
struct sigevent sev;
static void timerCallback(union sigval sv);

// 设置定时器事件,使用线程方式
sev.sigev_notify = SIGEV_THREAD;
// 设置回调
sev.sigev_notify_function = timerCallback;
//可以传递一个参数
sev.sigev_value.sival_ptr = params;

回调函数:

1
2
3
void timerCallback(union sigval sv) {
    MyStruct *params = (MyStruct *)(sv.sival_ptr); // 获取参数
}

信号模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <time.h>
#include <signal.h>
struct sigevent sev;
static void timerCallback(int signo, siginfo_t *si, void *data);

// 设置定时器事件,使用信号方式,仅发送一个SIGALRM信号
sev.sigev_notify          = SIGEV_SIGNAL;
sev.sigev_signo           = SIGALRM;
// 可以传递一个参数
sev.sigev_value.sival_int = params;

// 处理SIGALRM信号
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerCallback; // 设置回调
sigaction(SIGALRM, &sa, NULL);

回调函数:

1
2
3
4
static void timerCallback(int signo, siginfo_t *si, void *data) {
    int params = si->si_value.sival_int; // 获取参数
    // do something...
}

配置定时器触发时间

1
2
3
4
5
6
7
8
9
10
struct itimerspec its;
// 设置定时器间隔为1秒
its.it_value.tv_sec    = 0;
its.it_value.tv_nsec   = 1000 * 1000 * 1000; // 初始延时

// 如果是周期定时器,需要后续延迟
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 1000 * 1000 * 1000;
// 如果是单次定时器,不需要后续延迟
// its.it_interval.tv_nsec = 0;

创建定时器

1
2
3
4
5
6
7
8
#include <stdio.h> // for perror
timer_t timerid;
// 创建定时器,使用CLOCK_REALTIME方式保证实时性
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1)
{
    perror("timer_create");
    return 0;
}

启动定时器

1
2
3
4
5
// 启动定时器
if (timer_settime(timerid, 0, &its, NULL) == -1) {
    perror("timer_settime");
    return 0;
}

删除定时器

1
timer_delete(timerid);

注意事项

  • 注意在链接时加上 -lrt 参数。
  • 如果你的编译器默认未启用 Posix 支持,需要手动添加#define _POSIX_C_SOURCE 199309L
  • 还有个古老的 Posix 接口 setitimer,这里不再做介绍。
  • 信号模式下如果配置了信号将会导致同样使用该信号的功能失效,如 usleep 函数就会因为信号提前退出,可以考虑使用 nanosleep 函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    struct timespec req, rem;
    req.tv_sec  = ms / 1000;
    req.tv_nsec = ms % 1000 * 1000 * 1000;
    while (nanosleep(&req, &rem) == -1) {
        if (errno == EINTR) {
            // 因信号中断,重新设置休眠时间
            req = rem;
        }
        else {
            perror("nanosleep");
            return;
        }
    }
    
本文由作者按照 CC BY 4.0 进行授权

© Kai. 保留部分权利。

浙ICP备20006745号-2,本站由 Jekyll 生成,采用 Chirpy 主题。