第 8 章:Linux中使用时钟、计时器和信号

在本章中,我们将开始探索Linux环境中可用的各种计时器。随后,我们将深入了解时钟的重要性,并探讨UNIX时间的概念。接下来,我们将揭示在Linux中使用POSIX准确测量时间间隔的方法。之后,我们将进入std::chrono的领域,检查C++为有效的时间相关操作所提供的能力。我们的旅程接着将转向对std::chrono框架中定义的持续时间、时间点和时钟的全面检查。继续前进,我们将熟悉std::chrono内我们可以使用的各种时钟。在我们的探索路径中,我们将开始利用std::chrono提供的日历功能。在我们探索的最后阶段,我们将熟悉时区,并提升我们使用std::chrono的强大工具进行无缝时间转换的专业技能。

在本章中,我们将覆盖以下主要主题:

  • 探索Linux中的计时器
  • 处理C++中的时间
  • 使用时钟、计时器和比率
  • 使用日历和时区功能

那么,让我们开始吧!

技术要求

本章中的所有示例均在以下配置的环境中进行了测试:

  • Linux Mint 21 肉桂版。
  • GCC 13.2,编译器标志:-std=c++20
  • 稳定的互联网连接。
  • 请确保您的环境至少是这么新的。对于所有示例,您也可以替代使用 https://godbolt.org/。

在Linux中处理时间

计时是任何计算机系统的基本方面,Linux也不例外。在Linux中,有不同类型的计时器可用,每种设计用于处理特定的任务和需求。

这些计时器可用于测量程序的执行时间、安排任务、触发事件等。在本节中,我们将探索Linux中可用的不同类型的计时器以及如何有效地使用它们。

以下是Linux系统中使用的不同类型的计时器:

  • 系统计时器:Linux内核使用系统计时器来跟踪时间并安排各种任务。系统计时器用于测量系统运行时间、延迟和超时。Linux中最重要的系统计时器是 Jiffies 计时器,它在系统时钟的每个滴答中增加1。Jiffies计时器用于跟踪自系统启动以来的时间流逝,并且经常被各种内核模块和驱动程序使用。
  • 实时时钟(RTC):RTC是一个硬件时钟,即使系统关闭也会保持日期和时间的跟踪。Linux内核可以通过/dev/rtc设备文件或hwclock命令行工具读取和设置RTC。RTC用于在启动时同步系统时间,并为系统事件维护准确的时间戳。
  • 高分辨率计时器(HRTs):HRTs提供纳秒级分辨率,适用于需要精确计时的实时应用。HRTs可用于测量代码段的执行时间、高精度安排事件或驱动高速硬件。
  • POSIX计时器:POSIX计时器是POSIX标准定义的一组计时器,为Linux中的计时器管理提供统一接口。POSIX计时器可用于设置一次性或周期性计时器,这些计时器可以通过信号或线程触发。POSIX计时器是通过timer_create()timer_settime()timer_delete()系统调用实现的。
  • 计时器队列:计时器队列是Linux内核提供的一种机制,用于安排事件和超时。计时器队列作为事件的优先级队列实现,每个事件都与一个计时器相关联。计时器队列可用于安排周期性任务、实现超时或在特定间隔触发事件。计时器队列在各种内核模块和设备驱动程序中被广泛使用。

但在谈论计时器之前,我们首先需要理解计算机系统中时间的含义。让我们看一下。

Linux纪元

在计算中,纪元指的是用作特定系统或上下文中测量时间的参考点的特定时间点。它作为其他时间值计算或表示的起点。换句话说,这是计算机从中测量系统时间的时间。

纪元通常被定义为一个特定的时间点,通常表示为自特定纪元时间以来经过的秒数、毫秒数或其他比毫秒更小的时间间隔。纪元的选择取决于系统和上下文。例如,在UNIX-like系统中(Linux就是这样的系统),纪元定义为 1970年1月1日00:00:00 UTC(协调世界时间)。这个纪元时间通常被称为 UNIX纪元UNIX时间。在基于UNIX的系统中,时间值通常表示为自UNIX纪元以来经过的秒数。

现在,我们对UNIX纪元有了更好的理解,让我们看一些实际使用这些计时器的示例。

在Linux中使用计时器

既然我们已经知道Linux中可用的不同类型的计时器,让我们探索如何在我们的应用程序中使用它们。我们将看一个示例,它启动一个POSIX计时器并等待直到它被触发:

#include <iostream>
#include <csignal>
#include <unistd.h>
#include <sys/time.h>
#include <atomic>
static std::atomic_bool continue_execution{true};
int main() {
    struct sigaction sa{};
    sa.sa_handler = [](int signum) {
        // Timer triggered, stop the loop.
        std::cout << "Timer expired. Stopping the
          task...\n";
        continue_execution = false;
    };
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, nullptr);
    // Configure the timer to trigger every 1 seconds
    struct itimerval timer{
        .it_interval{.tv_sec{1}, .tv_usec{0}},
        .it_value{.tv_sec{1}, .tv_usec{0}}
    };
    // Start the timer
    setitimer(ITIMER_REAL, &timer, nullptr);
    std::cout << "Timer started. Waiting for timer
      expiration...\n";
    // Keep the program running to allow the timer to
      trigger
    while (continue_execution) {
        sleep(1);
    }
    return 0;
} 

在本示例中,我们定义了一个当计时器到期时将被调用的lambda处理函数。在处理函数中,我们打印出一个消息,指示计时器已到期,并设置忙碌循环的退出条件。

我们使用sigaction函数设置信号处理程序。然后,我们使用itimerval结构的it_intervalit_value成员配置计时器。配置计时器后,我们通过调用带有ITIMER_REAL选项的setitimer POSIX函数来启动它,该选项设置一个实时计时器,在到期时发送SIGALRM信号。我们进入一个循环以使程序无限期地运行。循环内的sleep(1)调用确保程序不会立即退出,并允许计时器触发。

程序的输出如下:

Program returned: 0
Timer started. Waiting for timer expiration...
Timer expired. Stopping the task... 

软件开发中的另一个常见任务是测量代码段的执行时间。这也可以通过使用POSIX时间功能来实现。要测量代码段的执行时间,我们可以在POSIX中使用HRT(高分辨率计时器)。

要在POSIX中使用HRT,我们将使用clock_gettime()函数以及CLOCK_MONOTONIC时钟ID。以下是一个演示在POSIX中使用HRTs的示例:

#include <iostream>
#include <ctime>
static const auto LIMIT{10000};
void just_busy_wait_f() {
    for (auto i{0}; i < LIMIT; ++i) {
        for (auto j{0}; j < LIMIT; ++j);
    }
}
int main() {
    timespec start, end;
    // Start the timer
    clock_gettime(CLOCK_MONOTONIC, &start);
    // Measured code segment
    just_busy_wait_f();
    // Stop the timer
    clock_gettime(CLOCK_MONOTONIC, &end);
    // Calculate the elapsed time
    const auto elapsed{(end.tv_sec - start.tv_sec) +
      (end.tv_nsec - start.tv_nsec) / 1e9};
    std::cout << "Elapsed time: " << elapsed << "
      seconds\n";
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值