当前位置:首页 > 文章列表 > 文章 > linux > Linux systemd socket activation 的监听交接

Linux systemd socket activation 的监听交接

来源:17golang原创 2026-10-17 08:21:16 0浏览 收藏

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 手册。

socket单元、监听地址、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以及业务连接fd的静态关系
结构说明图:sd_listen_fds 识别继承描述符,listener 与 client 是不同对象;accept 关联两者,但图中不表达执行顺序。

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。理解这两个生命周期,比把“端口可连接”当成“业务进程存在”的证据更重要。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux systemd 服务环境变量的 drop-in 配置Linux systemd 服务环境变量的 drop-in 配置
上一篇
Linux systemd 服务环境变量的 drop-in 配置
Linux procfs smaps_rollup 统计进程内存的读取方法
下一篇
Linux procfs smaps_rollup 统计进程内存的读取方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    500次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    558次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    587次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    539次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    349次使用