📘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/strict
ProtectHome=true
、PrivateTmp=true
、PrivateDevices=true
NoNewPrivileges=true
CapabilityBoundingSet=
、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 重新加载所有配置。 |