计时器机制允许您安排操作系统内核在预定时间过去时通知应用程序。 您通常会通过提供两条信息来使用它们。 首先,您需要指定计时器在通知之前需要多长时间。 其次,您需要准备一个回调函数,以便在该通知发生时采取行动。
定时器的传统方法
Linux 和基于 Unix 的系统中的定时器机制已经发展到满足各种需求。 不同的方法可以帮助您解决不同类型的问题。 但是,您经常会看到第一个版本的 警报() 机制仍在使用。
闹钟功能是使用定时器最简单的方法; 这是它的原型:
unsigned int alarm(unsigned int seconds);
使用此方法,您只能以整秒为单位指定时间。 当时间到了,操作系统会发送 SIGALRM 向您的应用程序发出信号。 要在您的应用程序中处理计时器的到期,您还应该定义一个回调函数。
下面是一个信号处理函数的例子:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
void timer_callback(int signum)
{
time_t now = time(NULL);
printf("Signal %d caught on %li", signum, now);
}
int main()
{
signal(SIGALRM, timer_callback);
alarm(1);
sleep(3);
return 0;
}
这段代码引发了一个 SIGALRM 信号后 1 第二。 如果您想将计时器延迟增加到五秒,只需调用 报警器(5) 反而。 要停止计时器,请传递一个值 0: 报警(0).
当时间到时,您使用的计时器将不会定期重新启动。 例如,如果你想再延迟一秒钟,你应该通过另一个调用来重新启动机制 警报().
尽管它易于使用,但这种方法有一些缺点:
- 一次只有一个计时器。
- 不支持定期定时器。
- 您只能以整秒的倍数给出时间段。
- 无法知道计时器上还剩多少时间。
Save 上面给出的示例代码为 报警器.c. 当你编译并运行它时,程序会调用 timer_callback 一秒钟后运行。 然后它将等待剩余的两秒钟,因为 睡觉(3) 行,然后终止。
$ gcc -o alarm alarm.c
$ time ./alarm
Signal 14 caught on 1653490465
real 0m1.004s
user 0m0.000s
sys 0m0.003s
使用 time 命令的原因是能够看到时间。 但是如果你看结果,总的运行时间不是三秒。 这是由于 SIGALRM 来自的信号 报警器(1) 当第一秒结束时,而 系统调用 由 sleep(3) 函数运行引起的。 当这个信号到达时,它会中断为 睡觉(3)。
使用间隔计时器
间隔计时器机制首次出现在 BSD 4.2 版中。 它后来被 POSIX 标准化。 与传统相比的主要优势 警报() 基于定时器的方法是:
- 提供微秒分辨率。
- 它允许在三种不同模式下更详细地控制时间测量。
- 可以设置一次并使其定期工作。
- 可以找出它在任何给定时刻存在多长时间。
用于间隔定时器操作的函数原型如下:
#include <sys/time.h>
int setitimer(int which, const struct itimerval *newValue, struct itimerval *oldValue);
int getitimer(int which, struct itimerval *value);
struct itimerval
{
struct timeval itInterval; // next value
struct timeval itValue; // current value
};
struct timeval
{
long tv_sec;
long tv_usec;
};
如果要设置间隔计时器,则需要使用 时间 结构。 您需要使用此结构作为第二个参数将值传递给 每周 功能。
例如,将通知您的应用程序 1 秒然后每 300 毫秒通知您的应用程序的间隔计时器可以设置如下:
struct itimerval newTimer;
struct itimerval oldTimer;
newTimer.itValue.tv_sec = 1;
newTimer.itValue.tv_usec = 0;
newTimer.itInterval.tv_sec = 0;
newTimer.itInterval.tv_usec = 300 * 1000;
setitimer(ITIMER_REAL, &newTimer, &oldTimer);
如果在设置新值之前有一个间隔定时器处于活动状态,它的值将被传送到 时间 赋予函数第三个参数的类型。
您可以使用间隔计时器机制设置三种不同类型的计时器。 在第一个参数中指定定时器类型 设置定时器 ():
定时器类型 | 信号 | 解释 |
---|---|---|
ITIMER_REAL | SIGALRM | 与应用程序花费的时间无关,按总运行时间计算。 |
ITIMER_VIRTUAL | SIGVTALRM | 仅计算应用程序在用户模式下运行的时间。 |
ITIMER_PROF | SIGPROF | 计算应用程序在用户模式和系统模式下花费的时间总和。 |
从这张表中可以看出 ITIMER_REAL 类型发送一个 SIGALRM 信号,就像 警报() 功能。
使用间隔计时器和 警报() 在同一个应用程序中会令人困惑。 虽然您可以对剩余时间进行第二次检查 获取定时器(),同时使用它们是没有意义的。
下面是一个定义信号处理函数的例子 调试头:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include "./debug.h"
void timer_callback(int signum)
{
struct timeval now;
gettimeofday(&now, NULL);
printf("Signal %d caught on %li.%03li ", signum, now.tv_sec, now.tv_usec / 1000);
}
int main()
{
unsigned int remaining = 3;
struct itimerval new_timer;
struct itimerval old_timer;
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 = 300 * 1000;
setitimer(ITIMER_REAL, &new_timer, &old_timer);
signal(SIGALRM, timer_callback);
while (sleep(remaining) != 0)
{
if (errno == EINTR)
debugf("sleep interrupted by signal");
else
errorf("sleep error %s", strerror(errno));
}
return 0;
}
上面的代码使用 睡觉() 功能等待三秒钟。 在此期间,间隔计时器运行,首先运行一秒钟,然后运行 300 毫秒的间隔。
为了更好地理解,保存并编译示例代码并命名为 间隔.c:
$ gcc -o interval interval.c
$ time ./interval
Signal 14 caught on 1653493614.325
debug: sleep interrupted by signal (main interval.c:36)
Signal 14 caught on 1653493614.625
debug: sleep interrupted by signal (main interval.c:36)
Signal 14 caught on 1653493614.925
debug: sleep interrupted by signal (main interval.c:36)
Signal 14 caught on 1653493615.225
debug: sleep interrupted by signal (main interval.c:36)
Signal 14 caught on 1653493615.525
...
从计时器运行后的输出可以看出,它每 300 毫秒调用一次回调函数。
但是,稍等片刻后,您会注意到应用程序并未终止。 它继续每 300 毫秒运行一次回调函数。 如果以毫秒为单位增加间隔值,您将看到应用程序终止。 这是因为使用区域 睡觉() 功能。
在 Linux 中使用定时器的重要性
特别是对于实时应用,定时器机制非常重要。 这也是用于性能优化的解决方案。 您甚至可以使用它来测量应用程序的正常运行时间或延迟。 使用计时器机制来跟踪经过的时间和时间转换事件是很重要的。