当前位置:首页 > 文章列表 > 文章 > linux > Linux systemd.timer 日历与单调时间的选择

Linux systemd.timer 日历与单调时间的选择

来源:17golang原创 2026-10-28 13:14:00 0浏览 收藏

Linux 的 systemd.timer 选错时间基准时,配置表面上能启动,到了校时、重启或挂起场景却可能出现“提前触发”“错过不补”或周期漂移。选择方法其实很明确:任务关心“几点几分、星期几”就用 OnCalendar=;任务关心“启动后经过多久、服务再次激活后多久”就用 OnBootSec=、OnActiveSec= 等单调时间指令。

官方地址:https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html

下面只围绕日历时间和单调时间的选择展开,并把挂起、系统校时、持久化补偿和触发精度放到同一个判断框架里。

先按任务语义区分两类时钟

OnCalendar= 使用墙上时钟,也就是系统的实时钟和时区规则。它适合“每天 02:00 清理”“每周一 09:30 汇总”这类固定民用时间。日历表达式由 systemd.time 解析,可以写星期、日期、时间和时区。

OnBootSec=、OnStartupSec=、OnActiveSec=、OnUnitActiveSec= 与 OnUnitInactiveSec= 则描述相对时长。它们不依赖当前是几点,也不因时区改变而改变语义,适合“机器启动 10 分钟后做一次”“服务每次激活后 15 分钟再触发”这类任务。

systemd.timer 日历时间与单调时间的静态关系结构图
图1:日历时间与单调时间的静态关系。日历表达式连接实时钟,启动或激活后的相对时长连接单调钟;精度和补偿是另外两层配置。

固定日期和周期日期写成 OnCalendar

日历任务的核心是把业务规则写成“下一次应落在哪个墙上时间”。例如夜间归档应该使用 OnCalendar=*-*-* 02:00:00,而不是启动后固定等待若干小时;否则主机中午重启时,归档时刻也会跟着漂移。

[Unit]
Description=夜间归档任务

[Timer]
# 固定在每天凌晨两点,表达业务日历而不是启动后的延迟
OnCalendar=*-*-* 02:00:00
# 机器离线时不运行,恢复后是否补跑由 Persistent 决定
Persistent=true
# 让同一主机上的多个定时器不要都挤在精确同一秒唤醒
AccuracySec=1min

[Install]
# 允许 systemctl enable 生成开机启动关联
WantedBy=timers.target

Persistent=true 只对 OnCalendar= 有意义:systemd 会保存上次触发时间,定时器重新激活时,如果停用期间本应触发过,服务会得到一次补偿触发。它不是“每个错过的周期都补一遍”,也不是单调计时器的通用补偿开关。

如果任务必须按某个地区的当地时间运行,把时区写进表达式比依赖主机默认时区更清楚:

[Timer]
# 用明确时区表达当地营业日,避免主机时区变化造成歧义
OnCalendar=Mon..Fri *-*-* 09:30:00 Asia/Shanghai
# 让系统在可接受的窗口内合并唤醒
AccuracySec=30s

启动后延迟使用 OnBootSec 或 OnStartupSec

“启动后等待一段时间”不是日历问题。OnBootSec=10min 相对系统启动计时,适合启动后预热、延迟清理和开机后的一次性准备。OnStartupSec= 相对 systemd 用户实例或系统管理器启动,具体使用哪一个取决于 timer 所在的管理器。

[Timer]
# 每次系统启动后等待十分钟,再触发同名服务
OnBootSec=10min
# 让 timer 被重新激活后也重新开始等待
OnActiveSec=10min
# 单调时间不是精确墙上时刻,按可接受的窗口合并唤醒
AccuracySec=30s

这里有一个容易忽略的边界:如果使用 OnBootSec= 或 OnStartupSec= 时,配置的目标时刻在 timer 激活时已经过去,systemd 可以立即触发服务;这和日历表达式错过某个墙上时刻的处理语义不同。需要严格的每日时刻时,不要用“启动后若干小时”代替 OnCalendar=。

按服务生命周期选择 OnUnitActiveSec 和 OnUnitInactiveSec

如果间隔应该跟着被触发服务的生命周期走,就用 OnUnitActiveSec= 或 OnUnitInactiveSec=。前者相对服务上次激活,后者相对服务上次进入非活动状态。它们表达的是服务关系,不是“主机每天某时刻”。

[Timer]
# 服务激活后两小时再安排下一次触发
OnUnitActiveSec=2h
# 服务停用后十五分钟触发一次后续处理
OnUnitInactiveSec=15min
# 两个表达式任意一个到期,都可以触发关联服务
AccuracySec=1min

这类配置适合缓存刷新、短周期同步或服务完成一轮工作后的再次调度。若服务运行时间可能长于间隔,要特别关注实际的再次触发行为;新版本 systemd 还提供了只针对 OnCalendar= 的 DeferReactivation=,不能把它当成单调计时器的通用“防重入”开关。真正需要互斥时,应在服务本身设计锁、幂等和失败恢复。

挂起与校时决定了边界行为

默认单调计时器使用暂停时也会停住的单调时钟,因此笔记本睡眠两小时后,OnBootSec=3h 并不一定按现实世界过去的三小时计算。设置 WakeSystem=true 后,systemd 会使用在挂起期间继续推进的时钟,并在硬件支持时唤醒系统。

[Timer]
# 需要覆盖挂起时间时,使用可在挂起期间继续推进的时钟
OnBootSec=3h
WakeSystem=true
# 唤醒能力依赖系统权限与硬件,不能替代业务层的幂等处理
AccuracySec=1min

反过来,备份、报表和账务结算通常关心民用时间,且可能需要补跑,这时更适合 OnCalendar= 加 Persistent=true。不要用 WakeSystem=true 去模拟“错过日历任务后补跑”,两者解决的是不同问题。

所有带 OnCalendar= 的 timer 都与系统实时钟有关。系统刚启动、时钟尚未完成校准时,准确时间任务应与 time-sync.target 的依赖关系保持清楚;如果只是接受大致单调的本地时间,不要为了形式强行等待远端时间服务。

systemd.timer 挂起校时和持久化边界结构图
图2:挂起、校时和错过执行的静态边界。WakeSystem 改变单调时钟的挂起行为,Persistent 只负责日历定时器重新激活时的补偿判断。

用 AccuracySec 和检查命令收束配置

systemd.timer 的到期并不等于一定在配置的那一微秒启动服务。AccuracySec= 默认允许一个时间窗口,systemd 会在窗口内安排唤醒,以合并多个定时器的开销;需要精确时刻时再把它调小,但也要接受更多唤醒和更高功耗。

提交前可以只检查表达式和最终展开的单元内容,不需要把定时器改成另一种时间基准:

# 规范化日历表达式,并查看下一次匹配时间
systemd-analyze calendar 'Mon..Fri *-*-* 09:30:00 Asia/Shanghai'

# 读取合并 drop-in 后的最终 timer 配置
systemctl cat archive.timer

# 查看当前状态、上次触发和下一次触发时间
systemctl status archive.timer

判断时可以记住这张速查表:

问题优先指令关键边界
每天固定几点执行OnCalendar=时区、校时、Persistent=
开机后等待一段时间OnBootSec=默认挂起会暂停,激活时刻已过去可能立即触发
timer 激活后等待OnActiveSec=相对 timer 激活,不是当天固定时刻
跟随服务上次激活或停用OnUnitActiveSec= / OnUnitInactiveSec=间隔跟服务生命周期变化
睡眠期间也要计时单调指令 + WakeSystem=true依赖硬件、权限和幂等设计

最终原则只有一句:把“业务时刻”交给 OnCalendar=,把“经过时长”交给单调计时器;再分别配置补偿、挂起和精度。只要先回答任务依赖哪一种时间,systemd.timer 的选择就不会被重启、时区或睡眠场景带偏。

常见追问

OnCalendar 和 OnBootSec 可以同时写吗?

可以。同一个 timer 中多个表达式任意一个到期都可以触发关联服务,但要确认这是不是业务真正需要的“多入口触发”,并为服务做好幂等。

Persistent=true 能补齐所有错过的运行吗?

不能。它用于日历定时器重新激活时判断停用期间是否至少错过过一次,不是历史任务队列,也不会把每个周期逐条回放。

AccuracySec=1s 就一定精确到秒吗?

不一定。它只是 systemd 定时器的调度窗口,仍受系统调度、服务启动耗时和更底层的时间机制影响。需要严格时间语义时,应在服务内部记录实际时间并设计容错。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux systemd.path 监听文件变化的触发配置Linux systemd.path 监听文件变化的触发配置
上一篇
Linux systemd.path 监听文件变化的触发配置
Linux systemd-escape 怎样把路径转换成实例化 unit 名称
下一篇
Linux systemd-escape 怎样把路径转换成实例化 unit 名称
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    755次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    753次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    834次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    800次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    634次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码