首页/系统/Linux 运维命令速查/socket 单元与按需启动
systemd 与服务42 条

socket 单元与按需启动

socket 激活:监听端口的 socket unit 先启动,首个连接到达才拉起对应 service(systemd 把监听 fd 传给它),降内存也提启动速度。

Linux 运维命令速查系统更新于 2026-10-13

命令是干什么的

socket 单元让 systemd 先替服务监听端口,等第一个连接到达时再把服务拉起来,这就是 socket 激活。好处是低频服务不必常驻内存,同时把启动顺序解耦:服务还没就绪时连接会被内核队列缓冲住而不是被拒绝。systemd 把已经建立的监听套接字通过文件描述符交给服务进程,所以服务本身不需要再调 bind,改成从环境变量里取这个描述符即可。

基本信息

适用平台所有 systemd 发行版(RHEL 7 起、Debian 9 起、Ubuntu 16.04 起)
单元文件myapp.socket 负责监听,myapp.service 或模板 myapp@.service 负责干活
关键指令ListenStream、ListenDatagram、Accept、Service、FileDescriptorName
查看命令systemctl list-sockets、systemctl status myapp.socket
应用侧要求不自行 bind,改读 LISTEN_FDS 与 LISTEN_PID,或用 sd_listen_fds
权限要求单元文件写入与管理需要 root

典型使用场景

  • 低频访问的管理接口和健康检查端点,没必要常年常驻占内存
  • 老式 inetd 风格的小服务迁移进来,用 Accept 让 systemd 负责收发连接
  • 启动竞态解耦:上游还没起来,客户端的连接也不会被直接拒绝
  • 小内存机器按需唤醒多个服务,把常驻进程压成有请求才启动
  • 服务升级窗口内 socket 继续监听,新连接排队等待而不是失败
  • 给只会使用已建立套接字的程序做一个统一入口

socket 与 service 单元

socket 与 service 单元
# /etc/systemd/system/myapp.socket
[Unit]
Description=myapp 按需唤醒的监听套接字
[Socket]
ListenStream=127.0.0.1:8081   # 监听地址与端口
Accept=no                     # no 表示只监听,连接交给同一个服务实例
FileDescriptorName=myapp      # 给套接字起名字,便于应用识别
[Install]
WantedBy=sockets.target       # 只启用 socket,服务由它按需拉起

# /etc/systemd/system/myapp.service
[Service]
ExecStart=/usr/local/bin/myapp --listen-fds   # 应用需支持从 systemd 继承套接字
# 不要写 WantedBy=multi-user.target,否则开机就常驻,失去按需意义

启用与验证步骤

启用与验证步骤
sudo systemctl daemon-reload              # 改过单元文件必须重载
sudo systemctl enable --now myapp.socket  # 只启用 socket,服务保持未启动
systemctl is-active myapp.service         # 首次访问前应为 inactive
curl -s http://127.0.0.1:8081/health      # 第一个请求触发服务被拉起
systemctl is-active myapp.service         # 再查已变成 active
systemctl list-sockets --no-pager | head  # 列出监听中的套接字
systemd-socket-activate -l 8081 /usr/local/bin/myapp   # 手工验证应用是否会用继承的 fd

list-sockets 输出逐列读法

列名示例含义与注意
LISTEN127.0.0.1:8081正在监听的地址与端口,方括号包围的 IPv6 表示全部地址
UNITmyapp.socket负责监听的 socket 单元名
ACTIVATESmyapp.service连接到来时触发的单元,这一列为空说明单元名没对上
Accept=no 适合有 systemd 库支持的应用,一个服务实例处理多个连接;Accept=yes 是 inetd 风格,每个连接拉起一个进程,服务要写成模板单元,连接从标准输入输出进出。
应用侧要表明自己会接管套接字:调用 sd_listen_fds 或直接读 LISTEN_FDS 与 LISTEN_PID 环境变量。不读这两个变量又自行绑定,就会出现端口被占用的报错。
改了 ListenStream 之后必须 daemon-reload;已经运行中的单元还要 restart 才会按新地址监听,只改文件不重载等于没改。

易错点

  • 服务本身不支持继承套接字,启动后又去 bind 同一个端口,日志里报地址已被占用:要么改造应用,要么改用 Accept=yes 让 systemd 收好连接再递进来
  • socket 和服务都设了开机启用,服务开机即启动,socket 激活沦为摆设,只启用 socket 单元才是正解
  • socket 单元里的服务名拼错或对应单元不存在,连接能建立却永远等不到响应,客户端表现为超时挂起,排查时先看 list-sockets 的 ACTIVATES 列
  • 改了单元文件忘记 daemon-reload,或只 reload 没重启 socket,状态显示 active 但端口其实没在按新配置监听
  • 服务被 mask 或条件判断不满足导致起不来,套接字却仍然接受连接,故障被掩盖,要先看服务单元的历史状态

数据来源与参考

  • man 5 systemd.socket
  • systemd 官方文档:socket activation

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌