systemd 服务与开机自启
脚本能跑只是第一步。真正的嵌入式设备要求:断电重启后程序自动拉起、崩溃了自动重来、日志有据可查。systemd 就是让树莓派"永不掉线"的守护者。
脚本能跑只是第一步。真正的嵌入式设备要求:断电重启后程序自动拉起、崩溃了自动重来、日志有据可查。systemd 就是让树莓派"永不掉线"的守护者。
.service 文件)。一个单元文件用 INI 格式描述:程序在哪、怎么启动、何时启动、崩溃怎么办。写好放到 /etc/systemd/system/,systemd 就接管它。start/stop/restart 启停服务,enable/disable 设置/取消开机自启,status 查看运行状态,daemon-reload 在改了单元文件后重新加载。管理服务的日常全靠它。Restart=always 让服务崩溃或退出后自动重启,RestartSec=5 设重启间隔。这是嵌入式设备可靠性的关键——程序偶发 bug 崩了也能自己爬起来,无需人工干预。配合 StartLimitBurst 防止无限崩溃重启。journalctl 查看,支持按服务、时间、优先级过滤,-f 实时跟踪。不用自己写日志文件——print 出来的东西都能在 journal 里查到,排障利器。multi-user.target 命令行多用户、network-online.target 网络就绪)。单元用 After=/Wants= 声明依赖顺序——比如联网的服务要 After=network-online.target,确保网络起来后才启动。crontab -e 编辑任务,五个字段(分 时 日 月 周)定义频率。适合简单周期任务:每天备份、每小时抓拍、每分钟上报。语法紧凑但可读性一般。.timer 单元触发 .service,优点是:日志进 journal、支持"错过补运行"(Persistent)、依赖管理、精确到秒。复杂/需可靠性的定时任务用它,简单的 cron 也够。直接 SSH 里 python3 app.py 跑,一断开连接程序就死了,重启更是无影无踪。对比看清 systemd 的价值:
假设有个传感器上报脚本 /home/pi/project/sensor.py,用虚拟环境跑。创建单元文件:
# 创建服务单元文件(需 sudo)
sudo nano /etc/systemd/system/sensor.service
# /etc/systemd/system/sensor.service [Unit] Description=传感器数据采集服务 After=network-online.target # 等网络就绪后启动 Wants=network-online.target [Service] Type=simple # 前台进程,最常用 User=pi # 以 pi 用户运行(不用 root) WorkingDirectory=/home/pi/project # 用虚拟环境里的 python(绝对路径) ExecStart=/home/pi/project/env/bin/python /home/pi/project/sensor.py Restart=always # 崩溃/退出后自动重启 RestartSec=5 # 重启前等 5 秒 [Install] WantedBy=multi-user.target # 开机(多用户模式)时启动
三段结构:[Unit] 描述与依赖、[Service] 怎么运行、[Install] 开机自启目标。保存后启用:
# 改了单元文件后必须 reload sudo systemctl daemon-reload # 立即启动服务 sudo systemctl start sensor # 设为开机自启(关键!否则重启后不跑) sudo systemctl enable sensor # 查看运行状态(绿色 active (running) 表示正常) sudo systemctl status sensor # 一步到位:启用并立即启动 sudo systemctl enable --now sensor
systemd 启动服务时环境极简,没有你 shell 里的 PATH。ExecStart 里的 python、脚本、参数都要写绝对路径(如 /home/pi/project/env/bin/python,不能只写 python3)。用虚拟环境时更要指向 env 里的解释器。写相对路径或裸命令是新手最常见的启动失败原因。
服务跑起来后,用 systemctl 控制、journalctl 看日志:
# === 服务控制 === sudo systemctl restart sensor # 重启服务 sudo systemctl stop sensor # 停止 sudo systemctl disable sensor # 取消开机自启 systemctl is-active sensor # 查是否在运行 systemctl is-enabled sensor # 查是否开机自启 # === 查看日志(journalctl)=== # 看某服务的全部日志 journalctl -u sensor # 实时跟踪(最常用,像 tail -f) journalctl -u sensor -f # 只看最近 50 行 journalctl -u sensor -n 50 # 看今天的、按时间过滤 journalctl -u sensor --since today journalctl -u sensor --since "2026-07-07 08:00" # 只看错误级别 journalctl -u sensor -p err
Python 的 print 默认有缓冲,日志可能延迟出现在 journal 里。在单元文件 [Service] 段加 Environment=PYTHONUNBUFFERED=1,或运行时用 python -u,强制无缓冲输出,日志实时可见。这对调试常驻服务非常重要。
树莓派资源有限,给服务加资源上限和重启限流,防止一个坏服务拖垮整机:
# 在 [Service] 段追加这些指令: # 内存上限 200M,超了会被 OOM 杀掉(保护系统) MemoryMax=200M # CPU 最多用 50% CPUQuota=50% # 防崩溃风暴:10 秒内崩溃超过 5 次就停止重试 [Unit] StartLimitIntervalSec=10 StartLimitBurst=5
周期性任务(每天备份、每小时抓拍)有两种做法。先看简单的 cron:
# 编辑当前用户的 crontab crontab -e # 五字段格式:分 时 日 月 周 命令 # ┌─分(0-59) ┌─时(0-23) ┌─日 ┌─月 ┌─周(0-7) # 每天凌晨 3 点执行备份脚本 0 3 * * * /home/pi/backup.sh # 每 10 分钟抓拍一张 */10 * * * * /home/pi/env/bin/python /home/pi/snap.py # 每小时整点记录温度到日志 0 * * * * /usr/bin/vcgencmd measure_temp >> /home/pi/temp.log # 开机时执行一次(@reboot) @reboot /home/pi/startup.sh
需要日志、可靠性、错过补运行时,用 systemd timer。它由一对 .service + .timer 组成:
# /etc/systemd/system/backup.service(要执行的任务,Type=oneshot)
[Unit]
Description=每日数据备份
[Service]
Type=oneshot
User=pi
ExecStart=/home/pi/backup.sh
# /etc/systemd/system/backup.timer(何时触发) [Unit] Description=每天 3 点触发备份 [Timer] OnCalendar=*-*-* 03:00:00 # 每天 3 点 Persistent=true # 关机错过了,开机补跑 [Install] WantedBy=timers.target
# 启用定时器(注意是 enable .timer 不是 .service) sudo systemctl daemon-reload sudo systemctl enable --now backup.timer # 查看所有定时器和下次触发时间 systemctl list-timers
| 对比项 | cron | systemd timer |
|---|---|---|
| 配置复杂度 | 低(一行) | 高(两个文件) |
| 日志 | 需自己重定向 | 自动进 journal |
| 错过补运行 | 不支持 | Persistent=true 支持 |
| 依赖管理 | 无 | 可依赖其他单元 |
| 适用 | 简单周期任务 | 需可靠性/日志/依赖 |
嵌入式设备无人值守,程序必须常驻、自愈、开机自启——这是 systemd 的活。写一个 .service 单元([Unit] 依赖 + [Service] 运行方式 + [Install] 自启目标),放到 /etc/systemd/system/,关键项:ExecStart 用绝对路径(虚拟环境指向 env/bin/python)、Restart=always 崩溃自愈、User=pi 非 root 运行、After=network-online.target 等网络。用 systemctl daemon-reload 加载、enable --now 启用并启动、status 看状态。日志全进 journal,journalctl -u 服务名 -f 实时跟踪(记得设 PYTHONUNBUFFERED=1)。资源上限用 MemoryMax/CPUQuota 保护整机。定时任务简单用 cron(crontab -e),要可靠性/日志/补运行用 systemd timer(.timer + .service)。下一章我们用 Docker 容器化部署,一键跑起 Home Assistant 等应用。