如何在c++中获取系统运行时间的毫秒数?

17

我如何获取系统自启动以来的运行时间?我只能找到自纪元以来的时间,没有别的东西。

例如,像ctime库中的time()一样的东西,但它只给我自纪元以来的秒数。我想要类似于time()但是从系统启动以来的时间。


4
视具体系统而定。 - dandan78
我是指自计算机诞生以来。我想获取系统计时器的值。有没有跨平台的方法可以做到这一点? - user3426112
@dandan:为了日后的用户参考,本帖子最好能够提供所有系统的答案。 - user1084944
3个回答

38

这取决于操作系统,并且在stackoverflow上已经为多个系统回答过了。

#include<chrono> // for all examples :)

Windows ...

使用 GetTickCount64() 函数获取系统启动时间(分辨率通常为10-16毫秒)

#include <windows>
// ...
auto uptime = std::chrono::milliseconds(GetTickCount64());

Linux ...

... 使用 /proc/uptime

#include <fstream>
// ...
std::chrono::milliseconds uptime(0u);
double uptime_seconds;
if (std::ifstream("/proc/uptime", std::ios::in) >> uptime_seconds)
{
  uptime = std::chrono::milliseconds(
    static_cast<unsigned long long>(uptime_seconds*1000.0)
  );
}

使用sysinfo(每秒一次的分辨率)

#include <sys/sysinfo.h>
// ...
std::chrono::milliseconds uptime(0u);
struct sysinfo x;
if (sysinfo(&x) == 0)
{
  uptime = std::chrono::milliseconds(
    static_cast<unsigned long long>(x.uptime)*1000ULL
  );
}

获取OS X系统运行时间 ...

... 使用sysctl

#include <time.h>
#include <errno.h>
#include <sys/sysctl.h>
// ...
std::chrono::milliseconds uptime(0u);
struct timeval ts;
std::size_t len = sizeof(ts);
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
if (sysctl(mib, 2, &ts, &len, NULL, 0) == 0)
{
  uptime = std::chrono::milliseconds(
    static_cast<unsigned long long>(ts.tv_sec)*1000ULL + 
    static_cast<unsigned long long>(ts.tv_usec)/1000ULL
  );
}

类似BSD系统(或支持CLOCK_UPTIMECLOCK_UPTIME_PRECISE的系统)...

...使用clock_gettime(分辨率请参见clock_getres

#include <time.h>
// ... 
std::chrono::milliseconds uptime(0u);
struct timespec ts;
if (clock_gettime(CLOCK_UPTIME_PRECISE, &ts) == 0)
{
  uptime = std::chrono::milliseconds(
    static_cast<unsigned long long>(ts.tv_sec)*1000ULL + 
    static_cast<unsigned long long>(ts.tv_nsec)/1000000ULL
   );
}

2
还有,有没有跨平台的方法来获取它? - user3426112
应该使用static_cast<unsigned long long>(uptime_seconds*1000),因为uptime_seconds包含毫秒,例如8084.13 * 1000 = 8084130 毫秒而不是8084 * 1000 = 8084000 毫秒 - Mr.Angel

3

+1 对被接受的答案。调查很好。但是 OS X 的答案是不正确的,我想在这里进行更正。

在 OS X 中,使用输入为 {CTL_KERN,KERN_BOOTTIME}sysctl 函数返回系统启动时的 Unix 时间,而不是从启动以来的时间。并且在这个系统(以及其他所有系统)中,std::chrono::system_clock 也测量 Unix 时间。因此,只需减去这两个时间点即可得到自启动以来的时间。以下是如何修改被接受的答案中的 OS X 解决方案:

std::chrono::milliseconds
uptime()
{
    using namespace std::chrono;
    timeval ts;
    auto ts_len = sizeof(ts);
    int mib[2] = { CTL_KERN, KERN_BOOTTIME };
    auto constexpr mib_len = sizeof(mib)/sizeof(mib[0]);
    if (sysctl(mib, mib_len, &ts, &ts_len, nullptr, 0) == 0)
    {
        system_clock::time_point boot{seconds{ts.tv_sec} + microseconds{ts.tv_usec}};
        return duration_cast<milliseconds>(system_clock::now() - boot);
    }
    return 0ms;
}

注意:

  • 最好让chrono为您执行单位转换。如果您的代码中有1000(例如将秒转换为毫秒),请重写以使chrono执行转换。
  • 如果隐式的chrono持续时间单位转换可以编译,则可以依赖其正确性。如果无法编译,这意味着您要求截断,您可以使用duration_cast显式地请求截断。
  • 如果使用using指令可以使代码更易读,则在函数中局部使用它是可以的。

system_clock 不是单调的,因此从 now-boot 中减去可能会导致错误甚至负值? - Adrian Maire
真的,但需要有人积极地试图破坏系统才能做到这一点。在正常情况下,计算机对Unix时间的跟踪会通过微小的时间调整来跟踪正确的时间,其精度类似于廉价石英钟。夏令时和改变时区都不会影响system_clock返回的值。严谨地说,在正常情况下,now-boot可能会有一点错误,因为macOS没有完美的时钟。 - Howard Hinnant
@Howard Hinnant:这需要有人更正系统时间。或者有人重新配置NTP源。或者是一个电池已经耗尽的系统(它们只能持续约10年),在引导过程中使用NTP来更正系统时间。不需要积极地试图破坏系统。 - Robin Davies

2

这里有一个关于如何自定义日志信息的boost示例

其中,作者正在实现一个简单的函数unsigned int get_uptime(),以获取不同平台(包括Windows、OSx、Linux和BSD)的系统运行时间。


2
其使用 GetTickCount 获取Windows时间, sysinfoclock_gettime 获取Linux时间,以及 sysctl 获取OS X时间。 - Pixelchemist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接