getitimer系统调用及示例
data-ad-format="fluid"
data-ad-layout-key="-7k+ex-4a-9w+4a">
继续学习 Linux 系统编程中的重要函数。这次我们介绍 getitimer 和 setitimer 函数,它们用于管理和控制进程的间隔计时器(interval timers)。这些计时器可以在指定的时间后产生信号(通常是 SIGALRM, SIGVTALRM, SIGPROF),从而实现定时执行代码、测量程序执行时间等功能。
本文介绍了 Linux 系统编程中的间隔计时器函数 getitimer 和 setitimer,它们用于管理和控制进程的定时器。这些计时器可以在指定时间后产生信号(如 SIGALRM),实现定时执行代码或测量程序执行时间等功能。setitimer 设置计时器的超时时间和重载时间,支持三种类型:ITIMER_REAL(实时)、ITIMER_VIRTUAL(用户态 CPU 时间)和 ITIMER_PROF(总 CPU 时间)。getitimer 则获取计时器的当前状态。文章还提供了示例代码,演示如何使用 ITIMER_REAL 和 SIGALRM 实现超时机制,并对比了相关函数如 alarm 和 POSIX 定时器 API。这些功能为系统编程提供了灵活的定时解决方案。
1. 函数介绍 getitimer 和 setitimer 是一对 Linux 系统调用,用于获取和设置进程的间隔计时器(interval timers)。
间隔计时器 (Interval Timer): 这是内核为每个进程维护的一个或多个计时器。每个计时器都有一个类型,当计时器超时(倒计时到 0)时,内核会向进程发送一个特定的信号。
setitimer: 设置指定类型计时器的超时时间和重载时间。
getitimer: 获取指定类型计时器的当前剩余时间(Value)和设置的重载时间(Interval)。
这提供了一种基于信号的定时机制,不同于 sleep 或 nanosleep 的主动睡眠,也不同于 alarm 的单一秒级定时器。
你可以把间隔计时器想象成一个可以设置闹钟和重复周期的高级闹钟:
2. 函数原型 1 2 3 4 5 6 7 8 #include <sys/time.h> // 必需 // 获取间隔计时器的当前设置 int getitimer(int which, struct itimerval *curr_value); // 设置间隔计时器 int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
3. 功能
getitimer: 查询由 which 指定的计时器的当前状态(剩余时间和重载时间),并将结果存储在 curr_value 指向的 struct itimerval 结构体中。
setitimer: 根据 new_value 设置由 which 指定的计时器。如果 old_value 非 NULL,则在设置新值之前,将计时器的旧值(设置前的剩余时间和重载时间)存储在 old_value 指向的结构体中。
4. 参数 共同参数 int which: 指定要操作的计时器类型。Linux 上有三种主要类型:
ITIMER_REAL: 实时计时器。
时钟源: 系统实时时间(墙上时钟时间)。
超时信号: SIGALRM。
用途: 测量真实世界流逝的时间。
ITIMER_VIRTUAL: 虚拟计时器。
ITIMER_PROF: 性能计时器(Profile Timer)。
setitimer 特有参数
struct itimerval 结构体 这两个函数都使用 struct itimerval 来表示时间间隔:
1 2 3 4 5 struct itimerval { struct timeval it_interval; // 重载时间 (Interval) struct timeval it_value; // 当前值/超时时间 (Value) };
其中 struct timeval 定义了秒和微秒:
1 2 3 4 5 struct timeval { time_t tv_sec; // 秒 suseconds_t tv_usec; // 微秒 (0 - 999,999) };
5. 返回值
6. 相似函数,或关联函数
alarm: 一个更简单的函数,只操作 ITIMER_REAL 计时器,且精度为秒。alarm(seconds) 大致等价于 setitimer(ITIMER_REAL, …),其中 it_value.tv_sec = seconds 且 it_interval.tv_sec = 0。
signal, sigaction: 用于设置当计时器超时时(收到 SIGALRM, SIGVTALRM, SIGPROF 信号)应执行的操作。
nanosleep, clock_nanosleep: 提供高精度的主动睡眠,不基于信号。
clock_gettime, clock_settime: 用于获取和设置各种系统时钟,更现代和灵活。
timer_create, timer_settime, timer_gettime: POSIX 定时器 API,功能更强大,支持多种时钟源和多种通知方式(包括信号和线程通知),是 setitimer 的现代替代品。
7. 示例代码 示例 1:使用 ITIMER_REAL 和 SIGALRM 实现超时 这个例子演示了如何使用 ITIMER_REAL 计时器在 3 秒后发送 SIGALRM 信号来中断一个可能长时间运行的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <sys/time.h> // setitimer, getitimer, ITIMER_REAL, struct itimerval #include <signal.h> // signal, SIGALRM #include <unistd.h> // pause, write #include <stdio.h> // printf, perror #include <stdlib.h> // exit #include <errno.h> // errno #include <string.h> // strerror volatile sig_atomic_t alarm_received = 0; // SIGALRM 信号处理函数 void alarm_handler(int sig) { // 在信号处理函数中,应只调用异步信号安全的函数 write(STDERR_FILENO, "Timeout! SIGALRM received.\n", 27); alarm_received = 1; } int main() { struct itimerval timer; const char *msg = "Doing some work that might take a long time...\n"; const char *msg_len = "Done.\n"; // 1. 设置 SIGALRM 信号处理函数 if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("signal SIGALRM"); exit(EXIT_FAILURE); } // 2. 配置 ITIMER_REAL 计时器 // it_value: 3 秒后超时 timer.it_value.tv_sec = 3; timer.it_value.tv_usec = 0; // it_interval: 0 表示一次性计时器,超时后不重载 timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; printf("Setting ITIMER_REAL to expire in 3 seconds.\n"); // 3. 启动计时器 if (setitimer(ITIMER_REAL, &timer, NULL) == -1) { perror("setitimer"); exit(EXIT_FAILURE); } // 4. 模拟一个可能长时间运行的操作 write(STDOUT_FILENO, msg, 46); // 5. 等待操作完成或超时 // 这里用 pause() 模拟等待,实际可能是 read(), write(), connect() 等阻塞调用 while (!alarm_received) { // 在实际应用中,这里可能是真正的阻塞操作 // 为了演示,我们用 pause() 等待信号 printf("Waiting for work to complete or timeout...\n"); pause(); // 进程挂起,直到收到信号 } write(STDOUT_FILENO, msg_len, 6); printf("Program finished after timeout.\n"); // 6. (可选) 检查计时器状态 struct itimerval curr_timer; if (getitimer(ITIMER_REAL, &curr_timer) == -1) { perror("getitimer"); } else { printf("Current ITIMER_REAL setting:\n"); printf(" it_value: %ld.%06ld seconds (should be 0)\n", (long)curr_timer.it_value.tv_sec, (long)curr_timer.it_value.tv_usec); printf(" it_interval: %ld.%06ld seconds\n", (long)curr_timer.it_interval.tv_sec, (long)curr_timer.it_interval.tv_usec); } return 0; }
代码解释:
定义一个全局的 volatile sig_atomic_t 变量 alarm_received 用于信号处理函数和主循环间通信。
定义 SIGALRM 的信号处理函数 alarm_handler。当计时器超时,内核会发送 SIGALRM 信号,该函数会被调用,打印消息并设置标志。
在 main 函数中,使用 signal() 注册 SIGALRM 处理函数。
定义一个 struct itimerval 变量 timer。
设置 timer.it_value 为 3 秒,表示 3 秒后第一次超时。
设置 timer.it_interval 为 0,表示这是一次性计时器。
调用 setitimer(ITIMER_REAL, &timer, NULL) 启动计时器。
模拟一个长时间运行的操作(这里只是打印一条消息)。
进入一个循环,等待操作完成或超时。这里用 pause() 模拟等待,实际应用中可能是 read, write, connect 等阻塞调用。
当 SIGALRM 信号到达,alarm_handler 被调用,设置 alarm_received 为 1,主循环退出。
程序结束。
最后,调用 getitimer 检查计时器的当前状态(超时后,it_value 应该接近 0)。
示例 2:使用 ITIMER_VIRTUAL 测量 CPU 时间 这个例子演示如何使用 ITIMER_VIRTUAL 来测量进程在用户态执行代码所花费的 CPU 时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <time.h> // clock() volatile sig_atomic_t vt_alarm = 0; void vt_alarm_handler(int sig) { write(STDERR_FILENO, "Virtual timer expired! (SIGVTALRM)\n", 35); vt_alarm = 1; } // 模拟一个消耗 CPU 的函数 void cpu_intensive_task() { volatile unsigned long sum = 0; for (unsigned long i = 0; i < 1000000000UL; ++i) { sum += i; } // 使用 sum 防止编译器优化掉循环 if (sum % 2 == 0) { // do nothing } } int main() { struct itimerval timer; if (signal(SIGVTALRM, vt_alarm_handler) == SIG_ERR) { perror("signal SIGVTALRM"); exit(EXIT_FAILURE); } // 设置 ITIMER_VIRTUAL 在 2 秒 CPU 用户态时间后超时 timer.it_value.tv_sec = 2; timer.it_value.tv_usec = 0; timer.it_interval.tv_sec = 0; // 一次性 timer.it_interval.tv_usec = 0; printf("Setting ITIMER_VIRTUAL to expire after 2 seconds of user CPU time.\n"); printf("Starting CPU-intensive task...\n"); if (setitimer(ITIMER_VIRTUAL, &timer, NULL) == -1) { perror("setitimer ITIMER_VIRTUAL"); exit(EXIT_FAILURE); } // 执行消耗 CPU 的任务 cpu_intensive_task(); if (vt_alarm) { printf("Task was interrupted by virtual timer.\n"); } else { printf("Task completed before virtual timer expired.\n"); } // 检查计时器状态 if (getitimer(ITIMER_VIRTUAL, &timer) == -1) { perror("getitimer ITIMER_VIRTUAL"); } else { printf("ITIMER_VIRTUAL status after task:\n"); printf(" it_value: %ld.%06ld seconds\n", (long)timer.it_value.tv_sec, (long)timer.it_value.tv_usec); printf(" it_interval: %ld.%06ld seconds\n", (long)timer.it_interval.tv_sec, (long)timer.it_interval.tv_usec); } return 0; }
代码解释:
设置 SIGVTALRM 信号处理函数。
配置 ITIMER_VIRTUAL 计时器,在进程使用 2 秒用户态 CPU 时间后超时。
调用 setitimer 启动计时器。
执行一个消耗大量 CPU 时间的函数 cpu_intensive_task。
如果在函数执行完毕前计时器超时,vt_alarm 标志会被设置,表示任务被中断。
最后检查计时器状态。
示例 3:使用 ITIMER_REAL 实现周期性操作 这个例子演示如何使用 ITIMER_REAL 实现一个周期性的“心跳”信号,每秒触发一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdatomic.h> // C11 原子操作 volatile atomic_int heartbeat_count = 0; void heartbeat_handler(int sig) { // 原子性地增加计数 atomic_fetch_add(&heartbeat_count, 1); // write 是异步信号安全的 write(STDERR_FILENO, "Heartbeat (SIGALRM)!\n", 21); } int main() { struct itimerval timer; int count_before_exit = 5; if (signal(SIGALRM, heartbeat_handler) == SIG_ERR) { perror("signal SIGALRM"); exit(EXIT_FAILURE); } // 设置周期性计时器:立即启动,每 1 秒触发一次 timer.it_value.tv_sec = 1; // 1 秒后第一次超时 timer.it_value.tv_usec = 0; timer.it_interval.tv_sec = 1; // 之后每 1 秒超时一次 (周期性) timer.it_interval.tv_usec = 0; printf("Setting periodic ITIMER_REAL (heartbeat every 1 second)...\n"); printf("Will exit after %d heartbeats.\n", count_before_exit); if (setitimer(ITIMER_REAL, &timer, NULL) == -1) { perror("setitimer periodic"); exit(EXIT_FAILURE); } // 等待指定次数的心跳 while (atomic_load(&heartbeat_count) < count_before_exit) { pause(); // 等待信号 } printf("Received %d heartbeats. Exiting.\n", count_before_exit); // 停止计时器 (设置所有时间为 0) struct itimerval stop_timer = {{0, 0}, {0, 0}}; if (setitimer(ITIMER_REAL, &stop_timer, NULL) == -1) { perror("setitimer stop"); } return 0; }
代码解释:
使用 atomic_int 和 atomic_fetch_add 来安全地在信号处理函数中增加计数。
配置 ITIMER_REAL 计时器:
调用 setitimer 启动周期性计时器。
在主循环中使用 pause() 等待信号。
每次收到 SIGALRM 信号,信号处理函数打印消息并增加计数。
当计数达到预定值时,主循环退出。
通过设置计时器的 it_value 和 it_interval 都为 0 来停止计时器。
示例 4:使用 setitimer 的 old_value 参数 这个例子演示如何使用 old_value 参数来保存和恢复计时器的旧设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> void alarm_handler(int sig) { write(STDERR_FILENO, "SIGALRM received.\n", 18); } void print_timer(const char *name, const struct itimerval *timer) { printf("%s:\n", name); printf(" Current value: %ld.%06ld seconds\n", (long)timer->it_value.tv_sec, (long)timer->it_value.tv_usec); printf(" Interval: %ld.%06ld seconds\n", (long)timer->it_interval.tv_sec, (long)timer->it_interval.tv_usec); } int main() { struct itimerval old_timer, new_timer; if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("signal SIGALRM"); exit(EXIT_FAILURE); } // 1. 设置一个初始的计时器 (5秒后超时,不重复) new_timer.it_value.tv_sec = 5; new_timer.it_value.tv_usec = 0; new_timer.it_interval.tv_sec = 0; new_timer.it_interval.tv_usec = 0; printf("Setting initial timer for 5 seconds.\n"); if (setitimer(ITIMER_REAL, &new_timer, NULL) == -1) { perror("setitimer initial"); exit(EXIT_FAILURE); } sleep(2); // 等待 2 秒 // 2. 获取当前计时器状态 (应该剩余约 3 秒) if (getitimer(ITIMER_REAL, &old_timer) == -1) { perror("getitimer before override"); exit(EXIT_FAILURE); } print_timer("Timer status after 2 seconds", &old_timer); // 3. 设置一个新计时器 (1秒后超时),并保存旧设置 new_timer.it_value.tv_sec = 1; new_timer.it_value.tv_usec = 0; new_timer.it_interval.tv_sec = 0; new_timer.it_interval.tv_usec = 0; printf("\nOverriding timer to expire in 1 second, saving old setting.\n"); if (setitimer(ITIMER_REAL, &new_timer, &old_timer) == -1) { perror("setitimer override"); exit(EXIT_FAILURE); } print_timer("Old timer setting saved", &old_timer); printf("Waiting for 1-second timer to expire...\n"); pause(); // 等待 1 秒计时器超时 // 4. 恢复旧的计时器设置 printf("\nRestoring old timer setting.\n"); if (setitimer(ITIMER_REAL, &old_timer, NULL) == -1) { perror("setitimer restore"); exit(EXIT_FAILURE); } if (getitimer(ITIMER_REAL, &new_timer) == -1) { perror("getitimer after restore"); exit(EXIT_FAILURE); } print_timer("Timer status after restore", &new_timer); printf("Waiting for restored timer to expire...\n"); pause(); // 等待恢复的计时器超时 printf("Restored timer expired. Program finished.\n"); return 0; }
代码解释:
设置一个 5 秒后超时的初始计时器。
等待 2 秒后,调用 getitimer 获取当前计时器状态(剩余约 3 秒)。
调用 setitimer(ITIMER_REAL, &new_timer, &old_timer) 设置一个 1 秒后超时的新计时器,并将旧计时器的设置(剩余约 3 秒)保存在 old_timer 变量中。
等待 1 秒计时器超时。
调用 setitimer(ITIMER_REAL, &old_timer, NULL) 将计时器恢复为之前保存的设置(约 3 秒)。
再次调用 getitimer 验证恢复是否成功。
等待恢复的计时器超时。
重要提示与注意事项:
精度: setitimer 的时间精度是微秒(tv_usec),而 nanosleep 和 POSIX 定时器 (timer_) 支持纳秒精度。对于高精度需求,后者是更好的选择。
信号处理: 使用 setitimer 必然涉及信号处理。必须小心编写信号处理函数,只使用异步信号安全的函数。
竞态条件: 在设置信号处理函数和启动计时器之间,或者在检查标志和调用 pause 之间,可能存在竞态条件。使用 sigsuspend 可以更安全地处理。
现代替代: timer_create, timer_settime 等 POSIX 定时器函数提供了更强大和灵活的功能,例如可以选择不同的通知方式(信号、线程特定信号、过期计数等)和不同的时钟源(CLOCK_REALTIME, CLOCK_MONOTONIC 等),是 setitimer 的推荐现代替代方案。
总结:
getitimer 和 setitimer 提供了一套基于信号的进程间隔计时器机制。它们可以用于实现超时、周期性任务和 CPU 时间测量等功能。理解三种计时器类型(ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF)及其对应的信号是掌握这些函数的关键。虽然它们功能强大,但在现代编程中,timer_ 系列的 POSIX 定时器通常被认为是更优的选择。
getitimer系统调用及示例-CSDN博客
https://www.calcguide.tech/2025/09/09/gettitimer-syscall-demo/