Linux time function in C/C++【2】

Linux 时间与定时机制设计思想的核心


一、时间函数的分类与作用

Linux 的时间函数大体分为两类:

(1)计时函数(获取当前时间)

这些函数用于读取当前的系统时间或高精度时间戳,区别在于时间单位与精度不同:

函数 返回结构 精度 说明
time() time_t(整型秒) 秒级 最简单、最老的API
ftime() struct timeb 毫秒级 已被废弃
gettimeofday() struct timeval 微秒级 常用,用户态实现,高效
clock_gettime() struct timespec 纳秒级 最精确,但调用代价略高

(2)定时函数(控制程序等待或定时触发)

这些函数用于让程序等待一定时间或设置定时器,常用于超时控制、周期任务等:

函数 类型 是否线程安全 是否基于信号 是否可与 I/O 复用机制结合
sleep() 秒级休眠 是(SIGALRM)
usleep() 微秒级休眠 是(SIGALRM)
nanosleep() 纳秒级休眠
alarm() 设置闹钟信号 是(SIGALRM)
getitimer() / setitimer() 定时器信号
timer_create() / timer_settime() POSIX 定时器 可选信号
timerfd_create() / timerfd_settime() 文件描述符定时器 可与 epoll/poll 集成

timerfd_* 系列,能直接与 Reactor 模型兼容。


二、为何选择 gettimeofday() 而不是 clock_gettime()

原因 1:精度与开销平衡

  • time() 只能到秒,不够;
  • ftime() 已被淘汰;
  • clock_gettime() 虽能到纳秒,但调用属于系统调用,会陷入内核,开销较高
  • gettimeofday() 精度达微秒,且 在 x86-64 平台是纯用户态实现(无系统调用陷入),因此速度极快。

也就是说:
clock_gettime() 追求极致精度;
gettimeofday() 追求低开销和足够的精度(微秒级即可满足绝大多数需求)。


原因 2:实现机制优越

在 x86-64 平台上,gettimeofday() 的实现利用了 VDSO(Virtual Dynamic Shared Object)机制,让用户态程序可直接访问内核的共享时间数据结构,无需切换上下文。



三、为何选择 timerfd_* 而非其他定时函数

原因 1:避免信号混乱

  • sleep()usleep()alarm()getitimer()setitimer() 等函数都依赖 SIGALRM 信号

  • 多线程程序中,信号机制非常危险:

    • 不确定信号由哪个线程处理;
    • 信号处理函数受限(不可调用大部分系统API);
    • 若主程序与第三方库都用 SIGALRM,极易发生冲突。

强调:多线程程序应尽量避免信号驱动定时。

  • 不确定信号由哪个线程处理:

    • 由于信号处理是异步且不可预测的,操作系统可能将信号随机分配给任意线程
    • 主线程和工作线程都可能成为信号处理者,导致程序行为不可控
    • 例如在Web服务器中,SIGTERM信号可能被处理请求的工作线程捕获,而非主线程
    • POSIX标准规定信号的处理线程是未指定的,不同实现表现各异
  • 信号处理会中断线程的正常执行流:

    • 信号处理函数可能在任何时刻抢占线程执行
    • 若信号发生在临界区内,可能破坏共享数据的一致性
    • 比如数据库写入操作被信号中断,导致数据损坏
  • 线程安全信号处理难度大:

    • 信号处理函数中只能使用异步信号安全函数
    • 常规的锁机制在信号处理中不可用
    • 需要复杂的线程间协调机制来保证安全
  • 信号处理与线程取消的交互问题:

    • 信号可能意外触发线程取消点
    • 导致资源清理和状态恢复更加困难
    • 在实时系统中这类问题尤为突出

原因 2:非阻塞编程要求"事件驱动"

  • nanosleep() 是线程安全的,但会让线程挂起
  • 会造成事件循环停滞
  • 正确做法:用定时器注册回调函数,让事件循环继续执行。

原因 3:timerfd 与 epoll完美结合

  • timerfd_create() 创建的定时器表现为一个文件描述符(fd)
  • 当定时到期,该 fd 就会"变得可读";
  • 因此它能直接与 select()poll()epoll() 结合;
  • 统一处理网络I/O事件与定时事件,形成优雅一致的事件循环机制

原因 4:精度更高

  • epoll_wait()poll() 的 timeout 参数是毫秒级;
  • timerfd_settime() 可达 纳秒级 精度。

因此,timerfd 提供了高精度、统一接口、线程安全的定时解决方案。


四、Linux 定时与计时的现实限制

在非实时 Linux 系统中,不可能做到"绝对精确"的计时。

原因是:

  • Linux 是非实时调度系统
  • 任意时刻,内核可能把当前任务换出;
  • 尤其在 CPU 负载高时,误差更明显。

因此我们能做的只是提高"时间精度的可靠性"

比如:

  • 控制 CPU 负载;
  • 使用合适的优先级;
  • 利用高精度 timerfd;
  • 保证程序在"99.99% 的情况下"按预期执行。

五、精度 vs 分辨率

最后两者区别:

  • Resolution(分辨率):时间函数能"分出多细的时间单位",比如微秒或纳秒;
  • Accuracy(准确度):返回值与真实时间的偏差;

举例:

你可能有一个 1 微秒分辨率的时钟,但在高负载时偏差达 100 微秒,这意味着分辨率高但准确度低。


六、总结对比表

功能类别 典型函数 精度 是否阻塞线程 是否依赖信号 是否线程安全 是否适合Reactor
计时 gettimeofday() 微秒
定时 sleep()
定时 nanosleep() 纳秒
定时 getitimer() 微秒
定时 timer_create() 纳秒 ✅可控
定时 timerfd_create() 纳秒

七、核心理念总结

  • 获取时间:gettimeofday() ------ 快、够精、用户态;
  • 定时任务:timerfd_* ------ 精度高、无信号干扰、易于与 epoll 集成;
  • Reactor 统一事件模型:IO 与定时都用"fd"驱动;
  • 非实时系统中追求高可靠时间精度而非"绝对精确"。

相关推荐
xlq223221 小时前
19.模版进阶(上)
c++
yuuki2332331 小时前
【C++】初识C++基础
c语言·c++·后端
小年糕是糕手1 小时前
【C++】类和对象(二) -- 构造函数、析构函数
java·c语言·开发语言·数据结构·c++·算法·leetcode
码龄3年 审核中1 小时前
Linux record 03
java·linux·运维
星驰云1 小时前
记一次CentOS 硬盘损坏无法启动修复教程
linux·运维·centos
人工智能训练1 小时前
windows系统中的docker,xinference直接运行在容器目录和持载在宿主机目录中的区别
linux·服务器·人工智能·windows·ubuntu·docker·容器
玫瑰花店1 小时前
SomeIP报文详解
c++·someip
q***47432 小时前
Windows 和 Linux 系统下,如何查看 Redis 的版本号?
linux·windows·redis
利刃大大2 小时前
【c++中间件】redis介绍 && redis-plus-plus库使用
c++·redis·中间件
代码对我眨眼睛2 小时前
Ubuntu 系统 NVIDIA 显卡驱动自动化安装全流程
linux·ubuntu·自动化