Linux systemd socket activation 的监听交接
systemd socket activation 的关键不是替程序多启动一个进程,而是让 .socket 单元持有监听套接字,再把对应文件描述符交给服务。使用 Accept=no 时,程序接收监听 fd,并自行 accept;它不应再次对同一地址 bind。使用 Accept=yes 时交付的是连接套接字,程序模型随之改变。
systemd 官方地址:https://systemd.io/。以下使用用户级单元、IPv4 回环地址和一个固定文本响应程序,只解释 .socket、.service 与 fd 继承,不实现公网服务或 HTTP 服务器。
先约定这个小项目做什么
目标是让本机客户端连接 127.0.0.1:18081 后收到一行 socket-note ready。端口由 socket-note.socket 管理,业务程序 socket-note-worker 只负责接受连接并回复。示例要求 Linux 上存在可用的 systemd 用户管理器、C 编译器、pkg-config 和 libsystemd 开发文件;开发包名称随发行版而异,应从发行版官方仓库安装。
用户级单元保存在 ~/.config/systemd/user/,程序放在 ~/.local/libexec/。这些是读者操作位置,不是已经部署好的产物。本文没有执行构建或启停命令,后文状态均是读者应观察的判断条件,不是实测记录。
由 .socket 描述地址,由 .service 描述程序
将下面内容保存为 ~/.config/systemd/user/socket-note.socket。ListenStream 负责地址,Accept=no 明确要求服务处理监听套接字。同名的 socket-note.service 是默认匹配对象,因此这里无需另写 Service=。
[Unit] Description=Local socket note listener [Socket] # 仅监听本机 IPv4,先确认这个示例端口没有被其他服务使用 ListenStream=127.0.0.1:18081 # 交付监听套接字,由业务程序接受连接 Accept=no [Install] WantedBy=sockets.target
再保存 ~/.config/systemd/user/socket-note.service:
[Unit] Description=Local socket note worker [Service] # 程序保持前台运行,不派生后台守护进程 Type=simple # %h 由 systemd 展开为当前用户的主目录 ExecStart=%h/.local/libexec/socket-note-worker
这个 service 不设置开机目标,也不需要单独 enable。启用的是 socket;连接到来时由它激活服务。Type=simple 不是业务就绪通知协议,不应把服务已被启动当作业务一定能响应。单元名称与 Accept 的关系见 systemd.socket 官方手册源文件,服务类型见 systemd.service 手册。

业务代码接管 fd,而不是重新创建监听端口
将以下完整代码保存为 socket-note-worker.c。sd_listen_fds(1) 读取激活协议并清理相关环境变量,返回收到的 fd 数量;fd 从 SD_LISTEN_FDS_START,即 3 开始。这里只允许一个 IPv4 流式监听套接字,数量或类型不符就退出,不静默退回自行监听。
#include#include #include #include #include #include #include int main(void) { // 清理激活环境;这个示例只接受一个继承的监听 fd int count = sd_listen_fds(1); if (count != 1) { fprintf(stderr, "expected one listener, got %d\n", count); return 1; } int listener = SD_LISTEN_FDS_START; // 最后一项为 0:不限制具体端口,但限定 IPv4、流式和监听状态 if (sd_is_socket_inet(listener, AF_INET, SOCK_STREAM, 1, 0)
程序没有调用 socket、bind 或 listen,因为监听对象已经由管理器准备好。接收方持有自己的 fd 引用,管理器仍可保留同一套接字的引用;“交接”不是把端口从管理器彻底搬走。对监听套接字做 shutdown 可能影响管理器持有的同一对象,因此不能用它代替关闭当前进程自己的 fd。
这是串行、短响应的教学代码,没有并发模型、完整写入超时或生产级连接治理;不要因为它只绑定回环地址,就把本机客户端一律视为可信。需要长期部署时,应另行设计资源限制和异常处理。

fd 数量、起始编号与共享监听对象规则见 sd_listen_fds 官方文档。如果以后增加多个 socket,不应继续假定“第一个必定是业务端口”,应按类型或名称识别,并重新定义数量约束。
在用户管理器中启用,并观察按需激活
读者准备好源码和两个单元后,可以在项目目录依次执行:
# 创建当前用户的单元和程序目录;单元内容需提前保存到对应位置 mkdir -p "$HOME/.config/systemd/user" "$HOME/.local/libexec" # 使用 libsystemd 的编译链接参数,生成前台工作程序 cc -Wall -Wextra socket-note-worker.c -o socket-note-worker $(pkg-config --cflags --libs libsystemd) # 只安装这个示例程序,不替换任何系统服务 install -m 755 socket-note-worker "$HOME/.local/libexec/socket-note-worker" # 加载两个新单元,只启用监听入口 systemctl --user daemon-reload systemctl --user enable --now socket-note.socket # 查看监听入口与业务进程各自的状态 systemctl --user status socket-note.socket socket-note.service # 用本机客户端发起连接;这是原始 TCP 文本,不是 HTTP nc 127.0.0.1 18081
应先看到 socket 处于监听状态;service 在首次连接前可能尚未启动,若已经被先前连接触发,也可能早已运行。客户端应收到代码定义的一行文本;如果没有,应根据日志确定问题,不能只凭端口可连接就宣布应用正常。
# 仅读取本示例用户级单元的近期日志,排查路径、链接或 fd 错误 journalctl --user -u socket-note.socket -u socket-note.service -n 40 --no-pager
用户管理器是否在退出登录后继续运行,取决于系统对该用户会话的管理策略,不能把这个实验等同于系统级常驻部署。所有命令都必须带 --user,避免把同名用户级与系统级单元混为一谈。
常见异常对应的是哪一层
| 现象 | 优先确认 | 不应直接采取的动作 |
|---|---|---|
| socket 无法启动 | 监听地址、端口占用和单元日志 | 杀掉不属于本项目的占用进程 |
| 程序报告没有继承 fd | 是否由 socket 激活、是否直接执行了二进制 | 手工伪造 LISTEN_PID 等环境变量 |
| 收到 fd 却类型不符 | Accept 模式与 socket 类型是否匹配 | 对任意 fd 盲目 accept |
| 程序再次 bind 时地址被占用 | 是否遗漏了继承分支 | 为了重复 bind 随意修改端口复用设置 |
不使用 shell 包装器启动示例,避免包装器未 exec 而改变实际接收进程,使 LISTEN_PID 与进程不匹配。直接执行二进制时返回非预期 fd 数量是这个示例的设计行为,不是要求添加一个隐藏监听分支。
结束实验时,同时考虑 socket 和 service
# 停止监听入口和已运行的业务进程,避免只停服务后又被连接激活 systemctl --user stop socket-note.socket socket-note.service # 取消这个示例的用户级自动启用,不删除任何其他单元 systemctl --user disable socket-note.socket
保留源码和单元便于继续阅读;这里不提供广泛删除目录的清理命令。若只修改 service 后重启进程,socket 仍运行时通常仍是原来的监听对象,这种引用保留不是“旧进程还活着”。它也不保证零丢失:排队容量、应用状态和客户端超时仍会影响请求。
把 Accept 改成 yes,代码可以不变吗?
不可以。yes 模式是按连接实例化服务,通常需要对应模板单元,并处理已连接套接字;本例明确要求监听状态,会拒绝这种交付方式。
socket activation 能让任意旧程序自动兼容吗?
不能。程序必须理解所用 fd 交付协议,或者明确支持另一种约定。只写两个单元,不会自动改造原来坚持自行 bind 的业务代码。
为什么停止 service 后仍然能连接端口?
因为监听生命周期属于 socket 单元。需要彻底结束入口时,应明确停止 socket;需要连业务进程一并结束时,同时停止 service。理解这两个生命周期,比把“端口可连接”当成“业务进程存在”的证据更重要。
Linux systemd 服务环境变量的 drop-in 配置
- 上一篇
- Linux systemd 服务环境变量的 drop-in 配置
- 下一篇
- Linux procfs smaps_rollup 统计进程内存的读取方法
-
- 文章 · linux | 47分钟前 |
- Linux /proc 读取进程内存指标的方法
- 194浏览 收藏
-
- 文章 · linux | 50分钟前 | 定时任务 · Linux · 日志排查 · Linux journalctl OnCalendar Persistent systemd timer cron迁移
- Linux 用 systemd timer 替代定时任务的迁移
- 253浏览 收藏
-
- 文章 · linux | 53分钟前 | Linux · Linux systemd 文件描述符 LimitNOFILE
- Linux 文件描述符上限的 systemd 配置清单
- 331浏览 收藏
-
- 文章 · linux | 53分钟前 | Linux ·
- Linux inotify 监控数量不足的参数调整
- 420浏览 收藏
-
- 文章 · linux | 55分钟前 |
- Linux ip route 处理多出口策略路由
- 204浏览 收藏
-
- 文章 · linux | 56分钟前 | Linux 网络隔离 路由表 network namespace veth ip netns
- Linux network namespace 验证隔离链路
- 443浏览 收藏
-
- 文章 · linux | 59分钟前 |
- Linux mount --bind 组织只读目录映射
- 452浏览 收藏
-
- 文章 · linux | 1小时前 | Linux 命名空间 pid util-linux 容器排障 lsns
- Linux lsns 怎样按类型和 PID 筛选命名空间
- 129浏览 收藏
-
- 文章 · linux | 1小时前 | Linux rss procfs smaps_rollup 进程内存 PSS
- Linux procfs smaps_rollup 统计进程内存的读取方法
- 394浏览 收藏
-
- 文章 · linux | 1小时前 | 环境变量 · Linux systemd Environment EnvironmentFile drop-in 服务环境变量
- Linux systemd 服务环境变量的 drop-in 配置
- 211浏览 收藏
-
- 文章 · linux | 1小时前 | 日志管理 · Linux journalctl 日志轮转 systemd-journald 持久化日志
- Linux systemd-journald 持久化日志的配置方法
- 180浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 500次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 558次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 587次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 539次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 349次使用
-
- Linux vmstat 如何分辨内存抖动与磁盘等待:si、so、wa 与复测顺序
- 2026-08-30 501浏览
-
- Linux搭建vsftpdFTP服务器教程
- 2026-04-30 501浏览
-
- Shell脚本安装教程:.sh一键安装指南
- 2026-03-16 501浏览
-
- Linux清空文件内容的几种方法
- 2025-12-01 501浏览
-
- Linux命令行下载文件技巧
- 2025-11-23 501浏览
