在Linux系统上做运维工具或者监控程序时,经常需要知道系统从开机到现在一共运行了多长时间。比如服务器健康检查、日志上报、心跳检测等场景,系统运行时长都是一个很基础但又必不可少的指标。Linux内核其实早就为我们准备好了答案,那就是/proc/uptime这个虚拟文件。本文用C++实战演示如何读取它并计算出可读的时间格式。

一、/proc/uptime文件的结构与原理
首先要明白,/proc目录并不是磁盘上真实存在的文件,它是内核在内存中动态生成的一棵虚拟文件树,被称为procfs。每次你用cat命令查看其中的文件,内核都会实时生成内容。所以读取/proc/uptime几乎没有任何磁盘IO开销,性能非常可观,这也是它被大量监控工具采用的原因。
/proc/uptime的内容非常简单,只有两个浮点数,格式如下:
cat /proc/uptime 86423.51 325611.09
第一个数字表示系统自启动以来经过的总秒数,精度可以到小数点后两位。第二个数字是所有CPU核心空闲时间的总和,注意是所有核心的累加值。举个例子,一台4核机器运行了100秒,如果这期间CPU基本处于空闲状态,第二个数理论上最大可以达到400秒左右。理解这一点对后面计算CPU空闲率非常重要。
另外需要说明的是,这里的秒数是系统真实的运行时长,不包含机器休眠挂起的时间差异问题,在绝大多数发行版上它的数值来源是内核的clock_gettime(CLOCK_BOOTTIME)计时,比手动记录开机时间戳更可靠。
二、C++读取与解析的完整实现
实现思路很朴素:用标准的C文件流打开/proc/uptime,读出两个double类型的数值,然后对第一个数做进制换算。下面是完整的可编译代码:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
struct UptimeInfo {
double totalSeconds; // 系统总运行秒数
double idleSeconds; // 所有CPU空闲时间总和
};
bool readUptime(UptimeInfo& info) {
std::ifstream file("/proc/uptime");
if (!file.is_open()) {
std::cerr << "无法打开 /proc/uptime" << std::endl;
return false;
}
file >> info.totalSeconds >> info.idleSeconds;
return file.good() || !file.eof();
}
int main() {
UptimeInfo info;
if (!readUptime(info)) {
return 1;
}
long long total = static_cast<long long>(info.totalSeconds);
int days = static_cast<int>(total / 86400);
int hours = static_cast<int>((total % 86400) / 3600);
int minutes = static_cast<int>((total % 3600) / 60);
int seconds = static_cast<int>(total % 60);
std::cout << "系统已运行: "
<< days << " 天 "
<< hours << " 小时 "
<< minutes << " 分 "
<< seconds << " 秒" << std::endl;
return 0;
}这段代码用了std::ifstream配合流提取运算符,直接把两个数字读进double变量,省去了手动字符串切分的麻烦。换算部分的核心就是86400(一天的秒数)、3600、60这三个进制常量的取模运算。需要注意的是秒数转整型时用了long long,如果机器运行了好几年,秒数会超过20亿,普通int会溢出。
如果你更喜欢传统C风格,也可以用fopen加fscanf的写法:
#include <cstdio>
int main() {
FILE* fp = fopen("/proc/uptime", "r");
if (fp == nullptr) {
perror("fopen failed");
return 1;
}
double up, idle;
if (fscanf(fp, "%lf %lf", &up, &idle) != 2) {
fclose(fp);
return 1;
}
fclose(fp);
printf("运行秒数: %.2f\n", up);
return 0;
}两种写法效果一致,C++流风格更安全,C风格则少了一层封装,在极端高频采样的场景下略微轻量一点。
三、替代方案:sysinfo函数与两种方式对比
除了读proc文件,Linux还提供了系统调用sysinfo,定义在<sys/sysinfo.h>中,结构体中直接有一个uptime字段,单位是秒:
#include <sys/sysinfo.h>
#include <iostream>
int main() {
struct sysinfo si;
if (sysinfo(&si) != 0) {
perror("sysinfo");
return 1;
}
std::cout << "运行秒数: " << si.uptime << std::endl;
std::cout << "空闲时间: " << si.idle << std::endl;
return 0;
}两种方式的差异主要体现在精度和细节上。sysinfo返回的是整型秒数,没有小数精度;而/proc/uptime保留两位小数,适合需要毫秒级差值计算的场景。此外sysinfo是一次系统调用,不经过VFS文件层,理论上单次调用更快,但一次系统调用的开销本身就远小于一次文件读取的差异,实际项目中两者性能都可以忽略不计。
还有一个隐藏的坑值得提醒:在容器环境中,比如Docker里读取/proc/uptime,得到的是宿主机的运行时长而不是容器的,因为procfs没有被命名空间完全隔离。如果你的程序需要感知容器自身的启动时间,应该改用cgroup或者记录进程自身的启动时间戳来做计算,这一点在云原生场景下尤其容易踩坑。
四、进阶:利用两个数值计算CPU空闲率
既然/proc/uptime给了我们总运行时长和总空闲时长,稍加推导就能算出系统启动以来的平均CPU空闲率,公式为:
#include <iostream>
#include <fstream>
int main() {
double up, idle;
{
std::ifstream f("/proc/uptime");
f >> up >> idle;
}
// 获取CPU核心数
unsigned int cores = std::thread::hardware_concurrency();
if (cores == 0) cores = 1;
// 空闲时间除以(总时长 × 核心数)得到空闲率
double idleRatio = idle / (up * cores);
std::cout << "平均CPU空闲率: " << idleRatio * 100.0 << "%" << std::endl;
return 0;
}注意分母必须乘以核心数,因为idle是所有核心的累加值。这个比率是自开机以来的历史平均值,如果想要实时CPU使用率,正确做法是间隔几秒采样两次,用两次采样之间的差值来计算,top、htop等工具内部都是这么实现的。
总结一下,读取/proc/uptime是C++获取Linux系统运行时间最通用、跨发行版最稳定的方案,代码简单、无第三方依赖、无磁盘开销。结合sysinfo作为备选,再注意容器环境的特殊性,基本可以覆盖所有实际项目的需求。
/proc/uptimeC++系统编程系统运行时间修改时间:2026-09-07 10:30:42