我们继续学习 Linux 系统编程中的重要函数。这次我们介绍 setitimer 函数的“伙伴”——getitimer 函数。虽然我们在介绍 setitimer 时已经涉及了 getitimer,但为了完整性和强调其重要性,我们再单独详细讲解一下。
1. 函数介绍 getitimer 是一个 Linux 系统调用,专门用于查询当前进程的间隔计时器(interval timers)的当前状态。它允许程序了解指定类型计时器的剩余时间(距离下次超时还有多久)和重载时间(超时后会自动重启的周期)。
你可以把它想象成查看你设置的闹钟或定时器上还剩多少时间。
2. 函数原型 1 2 3 4 #include <sys/time.h> int getitimer (int which, struct itimerval *curr_value) ;
3. 功能
4. 参数 int which: 指定要查询的计时器类型。与 setitimer 相同,主要有以下三种:
ITIMER_REAL: 实时计时器。时钟源是系统实时时间,超时发送 SIGALRM。
ITIMER_VIRTUAL: 虚拟计时器。时钟源是进程在用户态的 CPU 时间,超时发送 SIGVTALRM。
ITIMER_PROF: 性能计时器。时钟源是进程在用户态和内核态的总 CPU 时间,超时发送 SIGPROF。
struct itimerval *curr_value: 这是一个指向 struct itimerval 结构体的指针。getitimer 调用成功后,会将查询到的计时器当前状态信息存储到这个结构体中。
5. struct itimerval 结构体 这个结构体在 setitimer 和 getitimer 中都使用,用于表示计时器的时间设置:
1 2 3 4 5 struct itimerval { struct timeval it_interval; struct timeval it_value; };
其中 struct timeval 定义了秒和微秒:
1 2 3 4 5 struct timeval { time_t tv_sec; suseconds_t tv_usec; };
通过 getitimer 返回的 curr_value:
6. 返回值
7. 相似函数,或关联函数
setitimer: 用于设置间隔计时器。
getitimer 与 setitimer: 通常成对出现,用于查询和设置计时器。
alarm / setitimer: alarm(seconds) 大致等价于 setitimer(ITIMER_REAL, …)。
timer_gettime: POSIX 定时器 API 中用于获取定时器时间的函数,功能更强大。
clock_gettime: 用于获取各种系统时钟的当前时间。
8. 示例代码 示例 1:监控 ITIMER_REAL 的倒计时 这个例子演示了如何在启动一个 ITIMER_REAL 计时器后,使用 getitimer 在循环中监控其剩余时间。
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 #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> volatile sig_atomic_t alarm_fired = 0 ;void alarm_handler (int sig) { write (STDERR_FILENO, "Timer expired! SIGALRM received.\n" , 32 ); alarm_fired = 1 ; }int main () { struct itimerval timer, current_timer; int seconds_to_wait = 5 ; if (signal (SIGALRM, alarm_handler) == SIG_ERR) { perror ("signal SIGALRM" ); exit (EXIT_FAILURE); } memset (&timer, 0 , sizeof (timer)); timer.it_value.tv_sec = seconds_to_wait; timer.it_interval.tv_sec = 0 ; printf ("Setting ITIMER_REAL to expire in %d seconds.\n" , seconds_to_wait); if (setitimer (ITIMER_REAL, &timer, NULL ) == -1 ) { perror ("setitimer" ); exit (EXIT_FAILURE); } printf ("Monitoring timer countdown:\n" ); while (!alarm_fired) { if (getitimer (ITIMER_REAL, ¤t_timer) == -1 ) { perror ("getitimer" ); break ; } printf (" Time remaining: %ld.%06ld seconds\n" , (long )current_timer.it_value.tv_sec, (long )current_timer.it_value.tv_usec); usleep (500000 ); } if (alarm_fired) { printf ("Timer has fired. Program exiting.\n" ); } else { printf ("Loop exited before timer fired.\n" ); } return 0 ; }
代码解释:
设置 SIGALRM 信号处理函数。
使用 setitimer 启动一个 5 秒后超时的一次性 ITIMER_REAL 计时器。
进入一个 while 循环,循环条件是 alarm_fired 标志为假。
在循环内部:
调用 getitimer(ITIMER_REAL, ¤t_timer) 获取计时器的当前状态。
检查返回值,处理可能的错误。
打印 current_timer.it_value 中的剩余秒数和微秒数。
调用 usleep(500000) 延时 0.5 秒,然后继续下一次循环。
当 SIGALRM 信号到达,alarm_handler 被调用,设置 alarm_fired 为真,主循环退出。
示例 2:检查周期性计时器的状态 这个例子演示了如何查询一个周期性计时器的剩余时间和重载时间。
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <sys/time.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdatomic.h> #include <string.h> volatile atomic_int prof_count = 0 ;void prof_handler (int sig) { atomic_fetch_add (&prof_count, 1 ); write (STDERR_FILENO, "SIGPROF\n" , 8 ); }int main () { struct itimerval timer, status; const int period_sec = 2 ; const int num_intervals = 3 ; if (signal (SIGPROF, prof_handler) == SIG_ERR) { perror ("signal SIGPROF" ); exit (EXIT_FAILURE); } memset (&timer, 0 , sizeof (timer)); timer.it_value.tv_sec = period_sec; timer.it_interval.tv_sec = period_sec; printf ("Setting periodic ITIMER_PROF timer (every %d seconds).\n" , period_sec); if (setitimer (ITIMER_PROF, &timer, NULL ) == -1 ) { perror ("setitimer ITIMER_PROF" ); exit (EXIT_FAILURE); } printf ("Checking timer status immediately after setting:\n" ); if (getitimer (ITIMER_PROF, &status) == -1 ) { perror ("getitimer ITIMER_PROF" ); exit (EXIT_FAILURE); } printf (" Current value (remaining time): %ld.%06ld seconds\n" , (long )status.it_value.tv_sec, (long )status.it_value.tv_usec); printf (" Interval (reload time): %ld.%06ld seconds\n" , (long )status.it_interval.tv_sec, (long )status.it_interval.tv_usec); printf ("Waiting for %d intervals...\n" , num_intervals); int last_count = 0 ; while (atomic_load (&prof_count) < num_intervals) { sleep (1 ); if (getitimer (ITIMER_PROF, &status) == -1 ) { perror ("getitimer during loop" ); } else { int current_count = atomic_load (&prof_count); if (current_count != last_count) { printf (" After %d SIGPROF signals:\n" , current_count); printf (" Current value: %ld.%06ld seconds\n" , (long )status.it_value.tv_sec, (long )status.it_value.tv_usec); printf (" Interval: %ld.%06ld seconds (unchanged)\n" , (long )status.it_interval.tv_sec, (long )status.it_interval.tv_usec); last_count = current_count; } } } printf ("Received %d SIGPROF signals. Stopping timer.\n" , num_intervals); struct itimerval stop_timer = {{0 , 0 }, {0 , 0 }}; if (setitimer (ITIMER_PROF, &stop_timer, NULL ) == -1 ) { perror ("setitimer stop ITIMER_PROF" ); } printf ("Checking timer status after stopping:\n" ); if (getitimer (ITIMER_PROF, &status) == -1 ) { perror ("getitimer after stop" ); } else { printf (" Current value: %ld.%06ld seconds\n" , (long )status.it_value.tv_sec, (long )status.it_value.tv_usec); printf (" Interval: %ld.%06ld seconds\n" , (long )status.it_interval.tv_sec, (long )status.it_interval.tv_usec); } return 0 ; }
代码解释:
设置 SIGPROF 信号处理函数,并使用原子变量 prof_count 来计数。
使用 setitimer 启动一个每 2 秒触发一次的周期性 ITIMER_PROF 计时器。
立即调用 getitimer 检查并打印计时器的初始状态,可以看到 it_value 和 it_interval 都被正确设置。
进入循环等待 SIGPROF 信号。为了简化,循环中使用 sleep(1),但这并不会增加 ITIMER_PROF 的计时,因为 sleep 是墙钟时间,而 ITIMER_PROF 计算的是 CPU 时间。在实际性能分析中,这里应该是消耗 CPU 的工作。
在循环中定期调用 getitimer,并打印剩余时间。注意 it_interval 始终保持不变,因为它表示的是设置的周期。
接收到指定数量的信号后,通过设置 it_value 和 it_interval 都为 0 来停止计时器。
最后再次调用 getitimer,确认计时器已停止(值为 0)。
示例 3:结合 setitimer 的 old_value 和 getitimer 这个例子演示了如何结合使用 setitimer 的 old_value 参数和 getitimer 来管理计时器状态。
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 85 86 87 88 89 90 91 92 93 94 95 96 97 void alarm_handler(int sig) { write(STDERR_FILENO, "SIGALRM\n" , 8 ); } void print_timer_status(const char *msg, const struct itimerval *timer) { printf("%s:\n" , msg); printf(" Current value (remaining): %ld.%06ld seconds\n" , (long)timer->it_value.tv_sec, (long)timer->it_value.tv_usec); printf(" Interval (reload): %ld.%06ld seconds\n" , (long)timer->it_interval.tv_sec, (long)timer->it_interval.tv_usec); } int main() { struct itimerval initial_timer, old_timer, current_timer; if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("signal SIGALRM" ); exit (EXIT_FAILURE); } // 1 . 设置一个初始计时器 (10 秒后超时,不重复) memset(&initial_timer, 0 , sizeof(initial_timer)); initial_timer.it_value.tv_sec = 10 ; printf("Setting initial timer for 10 seconds.\n" ); if (setitimer(ITIMER_REAL, &initial_timer, NULL) == -1 ) { perror("setitimer initial" ); exit (EXIT_FAILURE); } sleep(3 ); // 等待 3 秒 // 2 . 使用 setitimer 的 old_value 参数获取并保存旧设置 struct itimerval temp_timer = {{0 , 0 }, {0 , 0 }}; // 新的临时设置 (这里设置为 0 ) printf("\nCalling setitimer to get old value (without changing timer).\n" ); // 通过设置一个 '0' 计时器并获取 old_value,可以读取当前状态 // 但这会取消当前计时器,不太符合 '只读' 的目的 // 更标准的 '只读' 方式是使用 getitimer // 让我们用更清晰的方式:先 getitimer,再用 setitimer 的 old_value printf("--- Correct way to get timer status ---\n" ); if (getitimer(ITIMER_REAL, ¤t_timer) == -1 ) { perror("getitimer" ); exit (EXIT_FAILURE); } print_timer_status("Status after 3 seconds (using getitimer)" , ¤t_timer); // 3 . 现在,设置一个新计时器 (5 秒),并保存旧设置 struct itimerval new_timer; memset(&new_timer, 0 , sizeof(new_timer)); new_timer.it_value.tv_sec = 5 ; // 5 秒后超时 printf("\n--- Setting new timer (5s) and saving old setting ---\n" ); if (setitimer(ITIMER_REAL, &new_timer, &old_timer) == -1 ) { perror("setitimer with old_value" ); exit (EXIT_FAILURE); } print_timer_status("Saved old timer setting" , &old_timer); sleep(2 ); // 再等待 2 秒 // 4 . 检查当前计时器状态 if (getitimer(ITIMER_REAL, ¤t_timer) == -1 ) { perror("getitimer current" ); } else { print_timer_status("Current timer status (after 2s of new timer)" , ¤t_timer); } printf("Waiting for new 5-second timer to expire...\n" ); pause(); // 等待 SIGALRM // 5 . 恢复旧的计时器设置 printf("\n--- Restoring old timer setting ---\n" ); if (setitimer(ITIMER_REAL, &old_timer, NULL) == -1 ) { perror("setitimer restore old" ); exit (EXIT_FAILURE); } if (getitimer(ITIMER_REAL, ¤t_timer) == -1 ) { perror("getitimer after restore" ); } else { print_timer_status("Timer status after restore" , ¤t_timer); } printf("Waiting for restored timer to expire...\n" ); pause(); // 等待恢复的计时器 SIGALRM printf("Restored timer expired. Program finished.\n" ); return 0 ; }
代码解释:
设置一个 10 秒后超时的初始计时器。
等待 3 秒后,调用 getitimer 获取并打印当前计时器状态(剩余约 7 秒)。
调用 setitimer(ITIMER_REAL, &new_timer, &old_timer):
等待 2 秒后,再次调用 getitimer 检查当前(新)计时器的状态(剩余约 3 秒)。
等待新计时器超时。
调用 setitimer(ITIMER_REAL, &old_timer, NULL) 将计时器恢复为之前保存的设置(约 7 秒)。
调用 getitimer 验证恢复是否成功。
等待恢复的计时器超时。
重要提示与注意事项:
“只读”查询: getitimer 是查询计时器状态的标准且推荐方式。虽然可以通过 setitimer 的 old_value 参数间接获取信息(例如设置一个 0 秒的定时器),但这通常会改变计时器状态(取消它),不符合“只读”查询的初衷。
精度: getitimer 返回的时间精度是微秒(tv_usec)。
时钟源: 返回的 it_value(剩余时间)是基于对应计时器的时钟源的。ITIMER_REAL 是墙钟时间,ITIMER_VIRTUAL 和 ITIMER_PROF 是 CPU 时间。
状态检查: getitimer 是检查计时器是否仍在运行以及还剩多少时间的有效方法。
与 setitimer 配合: getitimer 和 setitimer 经常一起使用,用于保存、恢复或动态调整计时器设置。
总结:
getitimer 是一个专门用于查询进程间隔计时器当前状态的系统调用。它通过填充 struct itimerval 结构体,返回指定计时器的剩余时间和重载时间。理解其与 setitimer 的配合使用对于精确控制和监控基于信号的定时任务至关重要。虽然 setitimer 的 old_value 参数也能提供一些信息,但 getitimer 是进行“只读”状态查询的标准和清晰的方法。
getitimer系统调用及示例-CSDN博客
相关阅读