📘systemd 单元文件
[[systemd]]
systemd 单元文件
systemd 的核心设计思想是将系统里的一切资源都抽象成“单元”(Unit)。单元是 systemd 管理的基本对象,涵盖了服务、套接字、设备、挂载点乃至系统状态等。
单元文件就是用来定义一个单元的纯文本配置文件。它清晰地描述了这个单元的属性、行为以及它与其他单元之间的关系。这些文件采用类似 INI 的语法,易于阅读和编写。
文件位置与优先级
systemd 会从多个目录中加载单元文件,并遵循一套明确的优先级规则
- 系统级(只读包):
/usr/lib/systemd/system/ - 系统级(本地管理员覆写):
/etc/systemd/system/ - 运行时(临时,重启丢失):
/run/systemd/system/ - 优先级:
/etc覆盖/run覆盖/usr/lib。同名时,高优先级目录生效。
推荐使用 systemctl edit <unit> 生成 drop-in 覆盖,避免直接改发行文件。
/etc/systemd/system/:最高优先级。系统管理员存放自定义或修改后的单元文件的地方。这里的配置会覆盖其他位置的同名文件。/run/systemd/system/:第二优先级。通常由程序在运行时动态创建单元文件。系统重启后该目录内容会丢失。- /usr/lib/systemd/system/
:最低优先级。通常由软件包管理器(如apt、yum`)安装的默认单元文件存放于此。不建议直接修改这里的文件。
核心原则:当需要修改一个默认单元文件时(例如 sshd.service),推荐使用 systemctl edit --full sshd.service 命令。它会自动将文件从 /usr/lib/systemd/system/ 复制到 /etc/systemd/system/ 并打开编辑器,确保你的修改具有最高优先级且不会在软件包升级时被覆盖。
systemd unit
定义行为、依赖与生命周期
管理的对象:服务、套接字、计时器、挂载点等。
常见类型:service、socket、target、timer、path、mount、automount、device、slice、scope。
单元命名与实例化
文件名格式:name.type,例如:nginx.service、sshd.socket。
模板单元:name@.type,实例化时用 name@instance.type,适合 per-connection 或 per-device。
你当前使用的是模板服务 dropbear@.service 配合 dropbear.socket 实现每连接派生。
单元文件结构
一个单元文件通常由几个配置段(Section)组成,每个段由 [方括号] 标识。其中,[Unit] 和 [Install] 是几乎所有单元类型都通用的。
[Unit]: 通用元信息、依赖、顺序。[Service]/[Socket]/[Timer]/…:类型专属的配置段。[Install]: 启用时的附着点(WantedBy/RequiredBy/Also)。
[Unit] 段:通用元数据与依赖关系
此段定义了单元的通用属性,它不关心单元的类型。
Description=: 一段描述性文本,用于在systemctl status等命令中直观地展示单元的用途。Documentation=: 提供文档链接,可以是man手册页或 URL,如man:sshd(8)或https://example.com/docs。After=: 定义启动顺序。此单元将在After列出的单元启动完成之后再启动。这是最常用的顺序依赖。Before=: 定义启动顺序。此单元将在Before列出的单元启动之前先启动。Wants=: 定义一种弱依赖关系。此单元启动时,systemd会尝试启动Wants列出的单元。但即使后者启动失败,也不影响此单元的启动。Requires=: 定义一种强依赖关系。此单元启动时,systemd必须成功启动Requires列出的单元。如果后者启动失败,此单元也将被停用。- Conflicts=
: 定义冲突关系。如果此单元启动,Conflicts` 中列出的单元将会被停止。反之亦然。
[Install] 段:启用与开机自启行为
此段定义了当用户使用 systemctl enable/disable 命令时,该单元应如何表现。
WantedBy=: 最常用的指令。表示此单元是WantedBy所指定的.target单元的“一部分”。执行systemctl enable时,systemd会在/etc/systemd/system/目录下创建一个指向此单元文件的符号链接,链接位于multi-user.target.wants/这样的目录中。RequiredBy=: 类似于WantedBy,但表示一个更强的依赖。如果RequiredBy指定的.target启动,此单元必须成功启动。- Also=
: 指定当此单元被启用/禁用时,Also` 列出的其他单元也应被一同启用/禁用。
主要单元文件类型详解
Service Unit (.service)
这是最核心、最常用的单元类型,用于定义一个系统服务(守护进程)。
主要用途:启动、停止、重启和管理后台服务进程。
核心配置段:[Service]
常用指令:
Type=: 定义服务的启动类型。simple(默认):ExecStart后的主进程就是服务进程。forking:ExecStart启动的进程会fork()一个子进程作为真正的服务进程,父进程会退出。systemd会等待父进程退出后认为服务启动完成。oneshot: 类似于simple,但systemd会等待主进程退出后才认为服务启动完成。适用于一次性任务(如内核模块加载、文件系统检查)。notify: 服务启动后会通过sd_notify()函数向systemd发送“准备就绪”的信号。dbus: 服务需要获取一个 D-Bus 名称。
ExecStart=: 指定启动服务时要执行的命令。ExecStop=: 指定停止服务时要执行的命令(可选)。ExecReload=: 指定重载服务配置时要执行的命令(可选)。Restart=: 定义服务在何种情况下应被自动重启。常用值有no(默认)、on-success、on-failure、on-abnormal、always。User=/Group=: 指定运行服务的用户和用户组。出于安全考虑,推荐使用非root用户。WorkingDirectory=: 指定进程的工作目录。Environment=/EnvironmentFile=: 为服务进程设置环境变量或从文件中加载环境变量。
Socket Unit (.socket)
用于定义一个网络套接字(TCP/UDP)或本地 IPC 套接字。它是 systemd 实现按需启动 (Socket Activation) 的关键。
主要用途:代替服务进程监听端口。当有连接请求时,systemd 会激活并启动相应的 .service 单元来处理该连接。
核心配置段:[Socket]
常用指令:
ListenStream=: 监听一个 TCP 套接字。值是端口号。ListenDatagram=: 监听一个 UDP 套接字。ListenFIFO=: 监听一个 FIFO (命名管道)。Accept=: 布尔值。false(默认) 表示systemd接受连接后,会启动一个服务实例并将该连接传递给它,然后该服务负责处理后续所有连接。yes表示systemd会为每一个进来的连接都启动一个新的服务实例(如dropbear@.service模板)。Service=: 指定当此套接字有活动时要激活的服务名。如果 socket 文件名是foo.socket,默认激活的服务就是foo.service。
Target Unit (.target)
用于对其他单元进行逻辑分组,本身不执行任何操作。它取代了传统 SysVinit 中的“运行级别”(Runlevel)。
主要用途:定义系统状态的同步点,管理一组相关的单元。
核心配置段:无专属配置段,主要通过 [Unit] 段的 Wants 和 Requires 来聚合其他单元。
常用 Targets:
multi-user.target: 类似于运行级别 3,用于启动所有网络服务,进入多用户命令行模式。graphical.target: 类似于运行级别 5,依赖multi-user.target,并额外启动图形界面服务。network.target: 表示网络已就绪。sockets.target: 所有.socket单元都应属于此目标。reboot.target: 用于重启系统。
Timer Unit (.timer)
用于定义定时器,是 cron 的现代化替代品,可以触发任何 .service 单元。
主要用途:定时或周期性地执行任务。
核心配置段:[Timer]
常用指令:
OnCalendar=: 基于日历的绝对时间。语法灵活,如daily,weekly,*-*-* 12:00:00(每天中午12点)。OnActiveSec=: 相对于timer自身被激活的时间。OnBootSec=: 相对于系统启动完成的时间。OnUnitActiveSec=: 相对于它所激活的单元最后一次被激活的时间。Unit=: 指定此定时器要触发的单元名。默认是同名的.service文件。Persistent=: 布尔值。如果设为true,当系统关机时错过的执行任务,会在下次开机后立即补上。
Path Unit (.path)
用于监控文件或目录的变化,当事件发生时触发另一个单元。
主要用途:实现基于文件系统事件的按需服务启动。
核心配置段:[Path]
常用指令:
PathExists=: 当指定路径存在时触发。PathChanged=: 当文件内容发生变化时触发。PathModified=: 当文件内容或元数据(如权限、时间戳)发生变化时触发。DirectoryNotEmpty=: 当指定目录从空变为非空时触发。Unit=: 指定要触发的单元。
Mount Unit (.mount) & Automount Unit (.automount)
.mount: 用于以声明式的方式管理文件系统的挂载点,可以替代 /etc/fstab。systemd 会在启动时自动将 /etc/fstab 条目转换为 .mount 单元。
.automount: 用于实现文件系统的按需挂载。它会监听一个挂载点目录,只有当该目录被访问时,systemd 才会真正执行挂载操作。
Slice Unit (.slice)
用于对一组单元进行资源限制。它基于 Linux 内核的 cgroups (控制组) 功能,本身不包含进程,而是作为一个资源控制的层级。
主要用途:对系统、用户或特定服务的 CPU、内存、I/O 等资源进行分组和限制。
核心配置段:[Slice],但资源限制通常直接在此段中定义。
常用指令:CPUWeight=, MemoryMax=, IOWeight= 等。
默认 Slice:system.slice (系统服务), user.slice (用户会话), machine.slice (虚拟机和容器)。
Scope Unit (.scope)
与 .service 类似,也用于管理一组进程。但 .scope 单元不是通过 systemd 启动进程,而是用于管理由外部进程(如用户登录、容器运行时)创建的进程。
Device Unit (.device)
由 systemd-udevd 服务自动创建,用于响应内核的设备热插拔事件。通常用户不需要手动创建。
Swap Unit (.swap)
用于定义和管理交换空间(swap分区或文件),可替代 /etc/fstab 中的 swap 条目。
Snapshot Unit (.snapshot)
一种特殊的单元,用于保存 systemd 管理器的当前状态(所有正在运行的单元)。可以用于临时进入一个不同的状态,然后再恢复到快照时的状态。
依赖与顺序的要点
Requires=强依赖;失败会导致依赖者失败。Wants=软依赖;失败不导致依赖者失败。Before=/After=仅控制顺序,不建立依赖。
套接字激活时,socket 与 service 的依赖/顺序由 systemd 根据命名自动处理。
条件与断言
ConditionPathExists=、ConditionKernelCommandLine= 等,条件不满足时跳过激活且不视为失败。
Assert*= 不满足时视为失败。
环境变量与配置注入
Environment="KEY=VAL"- EnvironmentFile=/etc/default/foo`(常见 Debian/BusyBox 习惯)
建议把可变参数放 EnvironmentFile,便于 OTA/不同机型差异化。
进程模型与 Exec
Type=simple:ExecStart进程为主进程Type=forking:适配传统 daemonize 程序(会 fork)Type=notify:进程通过 sd_notify 报告就绪ExecStartPre=/ExecStartPost=:前后钩子- TimeoutStartSec=` 避免卡启动
资源限制与安全沙箱
限制
User=、Group=、SupplementaryGroups=LimitNOFILE=、MemoryMax=、TasksMax=CPUQuota=、IOSchedulingClass=等
沙箱
ProtectSystem=full/strictProtectHome=true、PrivateTmp=true、PrivateDevices=trueNoNewPrivileges=trueCapabilityBoundingSet=、AmbientCapabilities=- RestrictAddressFamilies=
、RestrictSUIDSGID=、LockPersonality=`
对嵌入式建议:尽量开启 NoNewPrivileges、收窄 capability 集、文件系统 Protect、限制文件句柄与内存。
systemd 单元文件管理速查表
| 命令 | 用途 |
|---|---|
systemctl status <unit> |
查看单元的当前状态、日志摘要等详细信息。 |
journalctl -fu <unit> |
实时跟踪(follow)指定单元的日志输出。 |
systemctl cat <unit> |
查看单元的最终合成配置(包含所有 drop-in 文件)。 |
systemctl list-dependencies <unit> |
查看单元依赖的其他单元。 |
systemctl list-dependencies --reverse <unit> |
查看依赖此单元的其他单元。 |
systemctl start <unit> |
立即启动一个单元。只影响当前会话,不设置开机自启。 |
systemctl stop <unit> |
立即停止一个单元。只影响当前会话,不影响开机自启设置。 |
systemctl restart <unit> |
重启一个单元。 |
systemctl reload <unit> |
重新加载单元的配置(需要单元自身支持)。 |
systemctl enable <unit> |
设置开机自启。只创建符号链接,不会启动当前未运行的单元。 |
systemctl disable <unit> |
取消开机自启。只移除符号链接,不会停止当前正在运行的单元。 |
systemctl enable dropbear.socket |
示例:设置 dropbear 服务为开机 Socket 激活模式。 |
systemctl start dropbear.socket |
示例:立即开始监听 dropbear 的套接字(通常在 enable 后可选执行)。 |
systemctl edit <unit> |
使用 drop-in 文件覆盖部分配置(最佳实践)。 |
systemctl edit --full <unit> |
完整编辑单元文件,覆盖默认配置。 |
systemctl daemon-reload |
当手动修改磁盘上的单元文件后,执行此命令让 systemd 重新加载所有配置。 |