Linux下读取/proc/uptime可获取内核级开机总秒数(精确到小数点后两位),该值基于jiffies、不受NTP或休眠影响;应使用std::ifstream直接读取并stof转换,避免system调用;定时日志需用steady_clock校准间隔,写入须临时文件+fsync+rename保证原子性。

Linux 下读取 /proc/uptime 获取开机总秒数
Linux 系统的开机总时间(精确到小数点后两位)直接写在 /proc/uptime 文件里,第一列就是自启动以来的秒数。它不是 wall-clock 时间,而是内核 jiffies 累计的“系统运行时长”,不受 NTP 调整、休眠等影响 —— 所以比用 time() 算差值更可靠。
实操建议:
- 用
std::ifstream读取/proc/uptime,只取第一个浮点数(空格分隔),std::stof转换即可 - 别用
system("cat /proc/uptime")或管道,开销大且难捕获错误 - 注意权限:普通用户可读
/proc/uptime,无需 root - 该值是只读虚拟文件,不存在“打开失败但需重试”的场景,读失败基本等于路径不存在(非 Linux 系统)或进程被 chroot 隔离
C++ 中每分钟触发一次日志记录的稳定写法
不能靠 sleep(60) 硬等 —— 系统负载高、进程被调度暂停、日志写入卡顿都会导致累积误差,几分钟后就偏出十几秒。真正靠谱的是用单调时钟做间隔判断。
实操建议:
- 用
std::chrono::steady_clock记录上一次记录时间点 - 每次循环开头获取当前时间,与上次时间差 ≥ 60 秒才执行记录,并更新“上次时间”
- 避免把
sleep放在循环末尾;应计算剩余等待时间:std::this_thread::sleep_for(std::chrono::milliseconds(100))循环微等,再检查 - 日志写入(如
std::ofstream )必须加.flush(),否则缓冲区可能滞留,进程意外退出时丢最后一分钟数据
写入日志时防止覆盖、保证原子性的关键点
直接 ofstream.open("log.txt", ios::app) 追加看似安全,但多进程并发、磁盘满、信号中断都可能导致日志损坏或截断。实际部署中,最常踩的坑是“以为追加=绝对安全”。
实操建议:
- 不要直接写目标文件,先写临时文件(如
log.txt.tmp),写完fsync()+close(),再rename()原子替换(Linux 下rename是原子的) - C++17 起可用
std::filesystem::rename(),但需确认编译器支持(GCC 8+ / Clang 7+);否则用std::rename() - 每次写入前检查磁盘空间(
statvfs()),避免日志填满根分区导致系统异常 - 日志行格式建议为:
[2024-05-22T14:36:00] uptime=123456.78,带 ISO 时间戳,方便后期用awk或grep分析
跨平台兼容性问题:Windows 没有 /proc/uptime
这段代码如果未来要跑 Windows,/proc/uptime 会直接读失败。但 Windows 并不提供等价的“内核连续运行秒数”接口 —— GetTickCount64() 返回的是自开机起的毫秒数,但它会在系统运行约 49.7 天后溢出归零(虽是 64 位,但文档明确说“可能回绕”),且休眠期间会计时暂停。
实操建议:
- Linux 专用就明确限定平台,在 CMake 或编译宏里加
#ifdef __linux__,避免误导后续维护者 - 真要跨平台,改用
GetTickCount64()+ 启动时间校验(通过GetSystemTimes()获取 idle/ kernel/ user 时间,反推启动点),但逻辑复杂、权限要求高(需要SE_INCREASE_QUOTA_NAME) - 更现实的做法是:Windows 下 fallback 到记录进程启动后经过的时间(
steady_clock::now()差值),并注明“非系统 uptime”
真正难的不是读一个文件,而是让“每分钟一次”这件事在系统负载波动、日志 I/O 延迟、进程被挂起等各种真实场景下依然守时。多数人第一次写的定时循环,跑两天就会发现时间间隔变成 58 秒或 63 秒——那是因为没用 steady_clock 做基准,也没对 sleep 做补偿。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











