当前位置: 首页 > article >正文

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信号方式通知

当需要在定时器到期时触发一个信号,并且希望在信号处理器中执行特定的操作时使用。这是最常见的用法之一,适用于需要实时响应的应用程序

  1. 创建信号捕捉器sigaction
  2. 创建定时器timer_create
  3. 启动定时器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启动线程方式通知

当需要在一个新的线程中执行某个函数来处理定时器事件时使用。这种方式提供了更多的灵活性,因为你可以执行任意复杂的任务,而不受信号处理函数的限制。

  1. 创建线程函数
  2. 创建定时器timer_create
  3. 启动定时器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 可以传递额外的数据。

http://www.kler.cn/a/448205.html

相关文章:

  • 使用qemu搭建armv7嵌入式开发环境
  • Unity全局雾效
  • 《庐山派从入门到...》板载按键启动!
  • 学工管理系统-职校信息化管理平台
  • 京准电钟:电厂自控NTP时间同步服务器技术方案
  • 全脐点曲面当且仅当平面或者球面的一部分
  • 牛客网 SQL37查找多列排序
  • OpenWRT——官方镜像安装Docker(网络环境需设置)并配置Sun-Panel
  • 贪心算法解决分发糖果问题
  • 【Express】用express搭建本地服务器(轻松上手)
  • CSS系列(20)-- 自定义属性详解
  • 动态头部:利用注意力机制统一目标检测头部
  • 前端笔试面试题目——数据结构和算法篇(一)
  • 云手机能用来干什么?云手机在跨境电商领域的用途
  • SSM 架构上的 Vue 电脑测评系统:彰显科技评测魅力
  • XMLHttpRequest接受chunked编码传输的HTTP Response时有问题
  • 力扣第110题:平衡二叉树
  • MVVM、MVC、MVP 的区别
  • 前端篇-Content-Type 详解
  • 5G -- 空口关键技术
  • windows C#-实例构造函数
  • Linux基础(1)
  • JS里面Map的使用以及与Object的对比
  • 设计模式-读书笔记
  • 大数据——数据预处理
  • 【Spring】获取Bean对象需要哪些注解