libc timer

libc 提供了 timer_create, timer_settime, timer_delete 等函数来创建设置定时器。

创建定时器

使用 timer_create 函数可以创建一个定时器, 原型如下:

int timer_create(clockid_t clockid, struct sigevent *restrict evp,
           timer_t *restrict timerid);
  • clockid 用于指定测量时间的方式, 可以设为以下值:
    • CLOCK_REALTIME

      使用系统实时时钟

    • CLOCK_MONOTONIC

      一种不可设置的单调递增时钟,用于测量过去某个未指定点的时间,该时间点在系统启动后不会发生变化

    • CLOCK_PROCESS_CPUTIME_ID

      一个时钟,用于衡量(用户和系统)调用进程(所有线程)消耗的CPU时间

    • CLOCK_THREAD_CPUTIME_ID

      一个时钟,用于衡量(用户和系统)调用线程消耗的CPU时间

    • CLOCK_BOOTTIME

      CLOCK_MONOTONIC, 但不同的是: 在系统挂起后仍然测量时间

    • CLOCK_REALTIME_ALARM

      CLOCK_REALTIME 但会在系统挂起后唤醒系统, 调用者必须有 CAP_WAKE_ALARM 权限

    • CLOCK_BOOTTIME_ALARM

      CLOCK_BOOTTIME 但会在系统挂起后唤醒系统, 调用者必须有 CAP_WAKE_ALARM 权限

  • evp 用于设定定时器的处理方式及方法, evp..sigev_notify 指定处理方式, 可为以下值:
    • SIGEV_NONE

      当定时器到达时不做处理, 主动使用 timer_gettime 监听并处理

    • SIGEV_SIGNAL

      当定时器到达时发送 evp.sigev_signo 指定的信号

    • SIGEV_THREAD

      当定时器到达时, 开启一个新线程来调用 evp.sigev_notify_function 指定的函数

    • SIGEV_THREAD_ID

      当定时器到达时, 发送一个带有 evp.sigev_notify_thread_id 指定线程 id 的信号

  • timerid 定时器 id

链接时需要带上 -lrt


设置定时器

使用 timer_create 函数设置定时器的间隔时间, 原型如下:

int timer_settime(timer_t timerid, int flags,
           const struct itimerspec *restrict value,
           struct itimerspec *restrict ovalue);
  • timerid 创建得到的 id
  • flags 定时器标志, 设置为 TIMER_ABSTIME 时定时器下一次到达的时间为指定的绝对值与当前时钟的差值
  • value 设置定时器的时间间隔信息
  • ovalue 为上次设置的定时器间隔信息

定时器的时间间隔信息是 struct itimerspec 类型, 原型如下:

struct timespec {
    time_t tv_sec;                /* Seconds */
    long   tv_nsec;               /* Nanoseconds */
};

struct itimerspec {
    struct timespec it_interval;  /* Timer interval */
    struct timespec it_value;     /* Initial expiration */
};
  • it_value 为第一次定时器的间隔时间
  • it_interval 为第一次之后每次定时器的间隔时间
  • tv_sec 为秒数, 值应大于 0
  • tv_nsec 为纳秒数, 应小于 1000000000

删除定时器

使用 timer_create 函数可以删除一个定时器, 原型如下:

int timer_delete(timer_t timerid);
  • timerid 创建时得到的 id


示例如下

下面给出了使用信号和线程处理定时器的代码:

#include <time.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define CLOCKID CLOCK_REALTIME
#define SIG SIGALRM
#define TIMER_DURATION 1000 // 1000ms

static int start_signal_timer(timer_t *id, int sig, int duration);
static int start_thread_timer(timer_t *id, int duration);
static int do_start_timer(timer_t *id, struct sigevent *sev, int duration);
static int stop_timer(timer_t *id);
static void thread_handler(union sigval);
static int setup_signal(int sig);
static void signal_handler(int sig, siginfo_t *si, void *data);

int
main(int argc, char *argv[])
{
        if (argc != 2) {
                printf("Usage: %s <signal/thread>\n", argv[0]);
                return -1;
        }
        timer_t timerid;
        int ret = 0;
        if (strcmp(argv[1], "signal") == 0) {
                ret = setup_signal(SIG);
                if (ret == -1) {
                        printf("Failed to setup signal: %s\n", strerror(errno));
                        return -1;
                }
                ret = start_signal_timer(&timerid, SIG, TIMER_DURATION);
        } else if (strcmp(argv[1], "thread") == 0 ){
                ret = start_thread_timer(&timerid, TIMER_DURATION);
        }
        if (ret == -1) {
                return -1;
        }
        printf("Create timer id: %p\n", timerid);
        printf("Please input 'quit' to exit...\n");

        char buf[1024] = {0};
        while (1) {
                printf("Please input: ");
                scanf("%s", buf);
                if (strcmp(buf, "quit") == 0) {
                        break;
                }
        }

        ret = stop_timer(&timerid);
        if (ret == -1) {
                printf("Failed to delete timer: %s\n", strerror(errno));
        }
        printf("Exit...\n");
        return 0;
}

static int
start_signal_timer(timer_t *id, int sig, int duration)
{
        struct sigevent sev;

        // handle in thread when timeout
        memset(&sev, 0, sizeof(struct sigevent));
        sev.sigev_notify = SIGEV_SIGNAL;
        sev.sigev_signo = sig;
        sev.sigev_value.sival_int = 111;
        return do_start_timer(id, &sev, duration);
}

static int
start_thread_timer(timer_t *id, int duration)
{
        struct sigevent sev;

        // handle in thread when timeout
        memset(&sev, 0, sizeof(struct sigevent));
        sev.sigev_notify = SIGEV_THREAD;
        sev.sigev_notify_function = thread_handler;
        sev.sigev_value.sival_int = 111;
        return do_start_timer(id, &sev, duration);
}

static int
do_start_timer(timer_t *id, struct sigevent *sev, int duration)
{
        struct itimerspec its; // duration settings

        int ret = timer_create(CLOCKID, sev, id);
        if (ret == -1) {
                printf("Failed to create timer: %s\n", strerror(errno));
                return -1;
        }
        printf("The timer id: %p\n", id);

        // set timeout, only once
        // it_value the first timeout duration
        // it_interval the next timeout duration
        if (duration >= 1000) {
                its.it_value.tv_sec = duration / 1000;
                its.it_value.tv_nsec = (duration%1000) * 1000000;
        } else {
                its.it_value.tv_nsec = duration * 1000000;
        }
        its.it_interval.tv_sec = its.it_value.tv_sec;
        its.it_interval.tv_nsec = its.it_value.tv_nsec;

        ret = timer_settime(*id, 0, &its, NULL);
        if (ret == -1) {
                printf("Failed to set timeout: %s\n", strerror(errno));
                return -1;
        }

        return 0;
}

static int
stop_timer(timer_t *id)
{
        if (*id == 0) {
                return 0;
        }
        return timer_delete(*id);
}

static void
thread_handler(union sigval v)
{
        printf("Timer arrived: %d\n", v.sival_int);
}

static int
setup_signal(int sig)
{
        struct sigaction sa;

        sa.sa_flags = SA_SIGINFO;
        sa.sa_sigaction = signal_handler;
        sigemptyset(&sa.sa_mask);
        return sigaction(sig, &sa, NULL);
}

static void
signal_handler(int sig, siginfo_t *si, void *data)
{
        printf("Signal arrived: %d\n", sig);
        printf("\tUid: %u, Pid: %u\n", si->si_uid, si->si_pid);
        printf("\tValue: %d\n", si->si_value.sival_int);
}


参考链接:https://www.jianshu.com/p/dce281011799

posted @ 2021-10-15 16:33  不止所见  阅读(307)  评论(0编辑  收藏  举报