Skip to content

路线 B|C++ Linux 游戏服务端 ​

B1|Linux Engineering:从进程到可维护服务 ​

适用基础: Stage07 目标: 能在 Linux 上构建、运行、调试和优雅停止一个 C++ 服务。


一、真实问题 ​

服务在本地运行,部署后却“端口还在、请求不通”;或者进程被杀死后数据丢失、日志找不到、core 没有生成。Linux 服务工程首先是可观察、可停止、可复现,而不是只会写一个 while(true)。

二、进程、线程与文件描述符 ​

text
进程:地址空间、文件描述符、环境和信号边界
线程:共享地址空间,独立栈和调度状态
文件描述符:文件、Socket、管道等内核对象的进程句柄

/proc 是把进程状态映射给用户态的观察接口:status 反映线程和内存摘要,fd 展示文件描述符指向的内核对象,maps 展示虚拟内存映射。排查服务时应把 RSS、虚拟地址空间、线程数和 fd 数量分开看,不能只看一个“内存占用”。

三、Shell、权限与配置 ​

服务不应依赖当前目录和个人环境变量。使用绝对配置路径、最小权限用户和明确的工作目录。密码、Token 和生产 .env 不进入仓库。

四、构建、ELF 与调试 ​

bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
gdb --args ./build/server --config ./config/dev.toml

Linux 可执行文件通常经过预处理、编译、汇编、链接,最终形成 ELF;动态链接器在启动时解析共享库和符号。记录编译器、CMake、glibc、架构和 commit;线上 core 必须与同一构建符号匹配。Debug/Release 的优化、栈帧和内联差异会直接影响 GDB 观察结果。

五、日志与 systemd ​

日志至少包含时间、级别、进程/线程、请求或 session ID、错误码。systemd 单元应定义 User、WorkingDirectory、ExecStart、Restart、LimitNOFILE、TimeoutStopSec 和环境来源。优雅退出通过 SIGTERM,不使用 kill -9 作为正常发布流程。

1. 用户态与内核态 ​

C++ 代码通过系统调用请求内核创建进程、映射内存、读写文件和收发网络。系统调用会跨越权限边界,但“系统调用多”不必然是瓶颈;真正成本取决于是否阻塞、数据复制、上下文切换和设备等待。排查 CPU 高与 IO 慢时要先区分是在用户态计算,还是阻塞在内核或设备。

2. 虚拟内存 ​

每个进程看到独立虚拟地址空间,代码段、数据段、堆、栈和共享库映射到不同区域。RSS 表示当前驻留物理页,VSS 包含尚未实际占用的映射;内存映射、共享页和 allocator 缓存会让“进程内存”不能简单等同于业务对象总大小。

3. 信号的边界 ​

信号可能打断正常控制流,只有 async-signal-safe 操作适合直接在 handler 中执行。生产服务常用 self-pipe、eventfd 或原子标志把信号转成主循环事件,从而在正常线程上下文完成日志、锁、容器和资源清理。

4. 文件描述符与所有权 ​

fd 是进程表中的整数索引,复制 fd 会产生多个指向同一内核对象的引用。close 只释放当前引用;fork、dup、库缓存或泄漏都可能让资源继续存在。C++ 中应使用 RAII wrapper 明确 fd owner,避免异常和早退路径遗漏关闭。

六、服务生命周期知识模型 ​

服务的正常生命周期是:初始化配置和日志 → 绑定依赖 → 宣布 ready → 接收请求 → draining → 排空或取消任务 → 关闭 fd 和线程 → 退出。SIGTERM 是状态转换信号,不应在信号处理函数里直接执行复杂 C++、分配内存或写日志;通常只设置原子标志或写入自管道,再由主循环完成收束。

七、故障矩阵 ​

text
端口被占用 → ss/lsof 查 PID 和监听地址
启动即退出 → stderr、systemd status、动态库依赖
请求超时 → 监听 backlog、线程、网络策略、应用日志
内存上涨 → /proc、RSS、分配统计、业务缓存
停止卡住 → 线程栈、阻塞 IO、join 和退出状态机

八、练习与答案 ​

1. 为什么服务不能依赖 Ctrl+C? ​

生产环境由 supervisor/systemd 发送信号,服务必须明确处理 SIGTERM,并在限定时间内完成收束。

2. 为什么要限制文件描述符? ​

连接、日志和文件都会消耗 fd;上限过低会拒绝合法连接,泄漏则会耗尽资源。应监测使用量和增长趋势。

九、阶段输出 ​

提交一个可由 systemd 启停、能产生日志、能生成 core、能被 GDB 定位的 C++ 小服务。