Linux系统编程:告别 `alarm`,掌握高精度定时器 `setitimer`

  • 时间:2025-11-26 22:04 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:80T资源合集下载 链接:https://pan.quark.cn/s/5643428d4f9f 在 Linux 系统编程中,我们之前学习了 alarm 函数,它能提供秒级的定时功能。但在很多对时间精度要求较高的场景下(比如多媒体处理、游戏服务器帧率控制),秒级精度实在是“太粗糙”了。而且 alarm 是一次性的,想要循环触发必须重复调用。 今天我们要介绍一位更强大的“时间管理大师”——

80T资源合集下载
链接:https://pan.quark.cn/s/5643428d4f9f
在 Linux 系统编程中,我们之前学习了 alarm 函数,它能提供秒级的定时功能。但在很多对时间精度要求较高的场景下(比如多媒体处理、游戏服务器帧率控制),秒级精度实在是“太粗糙”了。而且 alarm 是一次性的,想要循环触发必须重复调用。

今天我们要介绍一位更强大的“时间管理大师”—— setitimer 函数。它不仅支持微秒(us)级精度,还自带周期性循环功能。

一、 为什么我们需要 setitimer

虽然 alarm 简单好用,但它有两个致命弱点:

精度低:只能精确到秒。非周期:触发一次后就失效,如果需要周期性触发(比如心跳包),需要在信号处理函数中再次调用 alarm,这会导致时间漂移。

setitimer 完美解决了这两个问题。

二、 函数详解

1. 函数原型


#include <sys/time.h>

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

2. 参数解析

参数 1: which(计时方式)

决定了定时器如何计时,以及超时后发送什么信号。虽然有三种,但实际开发中 99% 的情况只用第一种

ITIMER_REAL (最常用):自然计时法(墙上时钟)。无论进程是在运行、睡眠还是被切换,时间都会流逝。超时发送 SIGALRM ITIMER_VIRTUAL:只计算进程在用户态执行的时间。超时发送 SIGVTALRM ITIMER_PROF:计算进程在用户态 + 内核态执行的时间。超时发送 SIGPROF
参数 2: new_value(定时间隔)

这是最核心的参数,类型为 struct itimerval。它是一个嵌套结构体:


struct timeval {
    long tv_sec;  // 秒
    long tv_usec; // 微秒 (1秒 = 1000000微秒)
};

struct itimerval {
    struct timeval it_interval; // 周期性时间(间隔时间)
    struct timeval it_value;    // 第一次触发的时间(初始时间)
};
it_value:设置第一次定时器多久后触发。如果设为 0,表示关闭定时器。 it_interval:第一次触发后,每隔多久再次触发。如果设为 0,表示只触发一次(类似 alarm)。
参数 3: old_value(传出参数)

用于保存上一次设置的定时器剩余时间。通常传入 NULL 即可。

3. 返回值

成功:0失败:-1,并设置 errno

三、 实战演练

案例 1:实现周期性定时器

需求:程序启动后,等待 2 秒钟第一次触发,之后每隔 1 秒钟触发一次打印 “Hello World”。

注意:由于默认动作是终止进程,我们必须使用 signal 捕捉 SIGALRM 信号。

代码实现 (timer_loop.c):


#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

// 信号处理函数
void my_handler(int signo) {
    printf("Hello World! (收到信号: %d)
", signo);
}

int main() {
    // 1. 注册信号捕捉,否则收到 SIGALRM 进程会直接终止
    signal(SIGALRM, my_handler);

    struct itimerval new_t;

    // 2. 设置“周期”时间 (it_interval) -> 1秒
    new_t.it_interval.tv_sec = 1;
    new_t.it_interval.tv_usec = 0;

    // 3. 设置“第一次触发”时间 (it_value) -> 2秒
    new_t.it_value.tv_sec = 2;
    new_t.it_value.tv_usec = 0;

    // 4. 启动定时器,使用自然时间 (ITIMER_REAL)
    int ret = setitimer(ITIMER_REAL, &new_t, NULL);
    if (ret == -1) {
        perror("setitimer error");
        return -1;
    }

    printf("定时器已启动:2秒后首发,之后每1秒触发一次...
");

    // 保持进程运行
    while(1);

    return 0;
}

运行结果:


$ gcc timer_loop.c -o timer_loop
$ ./timer_loop
定时器已启动:2秒后首发,之后每1秒触发一次...
(等待2秒...)
Hello World! (收到信号: 14)
(等待1秒...)
Hello World! (收到信号: 14)
(等待1秒...)
Hello World! (收到信号: 14)
^C  <-- 按 Ctrl+C 结束

案例 2:用 setitimer 模拟 alarm

需求:实现一个 my_alarm(n) 函数,功能等同于 alarm(n),即 n 秒后触发一次,不循环。

代码实现 (my_alarm.c):


#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

// 简单的信号捕捉
void handler(int signo) {
    printf("BOMB! 定时器时间到!
");
}

unsigned int my_alarm(unsigned int seconds) {
    struct itimerval new_t, old_t;

    // 设置为不循环 (it_interval = 0)
    new_t.it_interval.tv_sec = 0;
    new_t.it_interval.tv_usec = 0;

    // 设置首次触发时间
    new_t.it_value.tv_sec = seconds;
    new_t.it_value.tv_usec = 0;

    // 启动定时器,并获取旧值到 old_t
    setitimer(ITIMER_REAL, &new_t, &old_t);

    // 返回旧定时器的剩余秒数
    return old_t.it_value.tv_sec;
}

int main() {
    signal(SIGALRM, handler);

    printf("启动 3 秒定时器...
");
    my_alarm(3);

    while(1);
    return 0;
}

运行结果:


$ ./my_alarm
启动 3 秒定时器...
(等待3秒)
BOMB! 定时器时间到!
(之后不再输出,因为 it_interval 为 0)

四、 知识小结与对比

特性alarm()setitimer()
精度秒 (s)微秒 (us)
触发模式单次触发单次 或 周期性循环
参数复杂度简单 (int)复杂 (嵌套结构体)
剩余时间获取通过返回值通过 old_value 参数
信号类型SIGALRMSIGALRM / SIGVTALRM / SIGPROF

避坑指南

结构体初始化:务必显式设置 tv_sec tv_usec。如果只设置秒,微秒字段包含随机垃圾值,可能会导致定时器立即触发或行为异常。信号处理:使用 setitimer 时,几乎总是需要配合信号捕捉( signal sigaction),否则默认动作通常是弄死进程。周期理解 it_value 决定什么时候开始 it_interval 决定频率。如果只想要单次定时,记得把 it_interval 设为 0。

setitimer 是 Linux 高级编程中处理时间的利器,虽然现在有了更现代的 timerfd 系列函数,但理解 setitimer 对于掌握操作系统的时间切片和信号机制依然至关重要。

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】交换机.路由器.防火墙-技术提升【4.3】(2025-11-26 22:52)
【系统环境|】交换机.路由器.防火墙-技术提升【4.2】(2025-11-26 22:51)
【系统环境|】交换机.路由器.防火墙-技术提升【4.1】(2025-11-26 22:51)
【系统环境|】交换机.路由器.防火墙-技术提升【4.0】(2025-11-26 22:50)
【系统环境|】交换机.路由器.防火墙-技术提升【3.9】(2025-11-26 22:50)
【系统环境|】i.mx8 HDMI显示分辨率异常(软件排查)(2025-11-26 22:49)
【系统环境|】Node.js环境变量配置实战(2025-11-26 22:49)
【系统环境|】交换机.路由器.防火墙-技术提升【3.8】(2025-11-26 22:48)
【系统环境|】交换机.路由器.防火墙-技术提升【3.7】(2025-11-26 22:48)
【系统环境|】10.MHA的部署(2025-11-26 22:47)
手机二维码手机访问领取大礼包
返回顶部