Systemd与Linux哲学

systemd与Unix哲学的关系是一个有趣的话题。

Linux 有一个基本原则:简单、可组合、让每个人都能参与。我们相信,最好的系统不是一个人设计出来的,而是通过成千上万人的实践打磨出来的。

systemd 也是这种思想的产物,但它体现的哲学值得聊聊:

每个工具做好一件事

systemd 之前,有 init、有 logger、有 cron、有网络管理…七八个独立工具,各管各的。systemd 做的是:把这些整合成一个统一的管理框架,用同一种语言(Unit)描述它们

这符合 Unix 的老传统:工具要解耦,但管理要统一

用文本说话

早期的 Unix 设计者有一个智慧:一切都是文本。日志是文本,配置是文本,管道里流动的也是文本。为什么?因为文本可以 grep、可以 awk、可以组合。

systemd 用 journal 统一日志,这是回到正道上来。

并行与依赖

Linux 内核最大的进步是调度器。systemd 学会了这一点:理解依赖,然后尽可能并行。这是现代系统该有的样子。

争议

社区对于 systemd 一直有争议。有人说它太复杂了,做了太多事。但这不是违背哲学——问题是它做得够不够好

Unix 哲学不是”不要变大”,而是”变大之后仍然保持可理解、可组合”。


用就完了。不满意?改它。如果你是真正的硬核——自己写一个。

这就是我们做事的方式。

在RaspberryPi部署

连接RaspberryPi并上传Python文件

连接至树莓派终端的方法繁多,在此不作赘述。笔者使用SSH进行远程登录,并且在此之前使用SFTP工具将上位机部分代码放至 /opt 文件夹下。

配置虚拟环境

使用虚拟环境管理每一次项目的依赖和解释器版本有利于保持目录的简洁和提高项目代码可读性(或许吧),防止项目依赖干扰系统依赖.尤其是OpenCV,
其前端窗口框架使用自带的QT框架,在linux特别是Wayland桌面环境下是冲突重灾区.

由于树莓派通常运行 Raspberry Pi OS(基于Debian),推荐使用Python自带的 venv 创建虚拟环境:

1
2
3
4
5
6
7
8
9
10
# 创建虚拟环境
cd /opt/your_project
python3 -m venv venv

# 激活并安装依赖
source venv/bin/activate #如果在Windows则是运行同目录的 activate.ps1
pip install -r requirements.txt

#如果没有环境依赖文件
pip install 包名

编写 systemd Service 配置

在进行Service的配置前需要一个趁手的文本编辑工具,笔者在这里推荐VIM和NANO(更适合新手),在熟悉后可转向使用VIM.

NANO的下载方法:

1
2
sudo apt upgrade && sudo apt update #更新软件源
sudo apt install nano

nano的使用方法如下:

1
2
nano /path/to/your/文件名 #当文件名或路径不存在时nano会自动创建
sudo nano /path/文件名 #使用sudo提权后所写文件在普通权限下无法更改

nano安装好后就可以开始配置Service,使用命令:

1
nano /etc/systemd/system/service_name.service

/etc/systemd/system/ 下创建服务文件,注意service_name替换为自己所起的服务命名,例如python_main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Unit]
Description=Python Main Service
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/your_project
# 激活虚拟环境
ExecStart=/opt/your_project/venv/bin/python /opt/your_project/main.py
Restart=on-failure
RestartSec=5

# 环境变量(如有需要)
Environment=PYTHONUNBUFFERED=1

# 日志管理
StandardOutput=journal
StandardError=journal
SyslogIdentifier=python_main

[Install]
WantedBy=multi-user.target

常用管理命令

在配置完成后需要使用systemctl工具赋予其执行权限.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 重新加载配置
sudo systemctl daemon-reload

# 启动服务
sudo systemctl start python-main

# 设置开机自启
sudo systemctl enable python-main

# 查看服务状态
sudo systemctl status python-main

# 查看日志
sudo journalctl -u python-main -f # 实时日志
sudo journalctl -u python-main --since "1 hour ago" # 最近一小时
sudo journalctl -u python-main -n 100 # 最近100条

# 重启/停止
sudo systemctl restart python-main
sudo systemctl stop python-main

配置成功后,Python程序将会在树莓派上电后自行启动,无需手动远程执行,在比赛中途异常抛出导致程序中断也可以自行重新启动,提高了小车的存活率(bushi)

调试技巧

在编辑Service文件时可以先手动执行启动命令进行测试.
使用journalctl工具可以查看进程日志.

1
2
3
4
5
6
7
8
9
# 手动运行测试
sudo -u youruser /opt/your_project/venv/bin/python /opt/your_project/main.py

# 查看详细错误
sudo journalctl -u python-main -xe

# 限制日志大小(如需持久化存储)
sudo journalctl --rotate
sudo journalctl --vacuum-time=7d

权限与运行用户

原则上不用root运行服务是一个好习惯.使用User和Group指定运行用户,可以避免被hack后拥有root权限造成更大损失.

1
2
3
[Service]
User=pi
Group=pi

记得确保项目文件的所有权属于该用户,否则会写出不来:

1
sudo chown -R pi:pi /opt/your_project

服务依赖

如果你的Python程序需要等待其他服务就绪(比如数据库、网络),可以使用After和Wants指定依赖关系:

1
2
3
4
[Unit]
Description=Python Main Service
After=network.target mysql.service
Wants=mysql.service
  • After: 在指定服务之后启动
  • Wants: 弱依赖,启动失败不影响本服务
  • Requires: 强依赖,启动失败本服务也停止

资源限制

树莓派资源有限,防止OOM是必要的.可以通过systemd限制内存和CPU:

1
2
3
[Service]
MemoryMax=512M
CPUQuota=50%
  • MemoryMax: 最大内存使用,超出后会触发OOM
  • CPUQuota: CPU使用上限,50%表示最多用半个核

启动次数限制

如果程序频繁崩溃,systemd会在短时间内反复重启,这样既没用还会消耗资源.使用StartLimitBurst限制重启次数:

1
2
3
4
5
[Service]
Restart=on-failure
RestartSec=5
StartLimitBurst=3
StartLimitIntervalSec=60

上述配置表示:60秒内最多重启3次,超过后不再尝试.结合RestartSec=5,给了程序足够的喘息时间来恢复正常.

自动恢复策略

根据业务需求选择合适的Restart策略:

1
2
3
4
5
6
7
[Service]
# 任何退出都重启
Restart=always
# 只有异常退出才重启(推荐)
Restart=on-failure
# 正常退出也重启
Restart=on-success

对于电赛小车,”on-failure”通常是最佳选择——正常退出不一定需要重启,但崩溃了一定要恢复.


至此,你的Python程序已经是一个合格的systemd服务了.它有自己独立的运行环境,有保护机制,有日志输出,在后台默默运行,沉默且可靠.快把这套程序部署起来吓哭你的电控队友罢