没有真正的统一管理,必须为Linux systemd和Windows服务分别适配:封装业务逻辑为ServiceCore类,Linux用systemd_main()处理SIGTERM并重定向IO,Windows用windows_service_main()响应SERVICE_CONTROL_STOP;日志需分别调用sd_journal_print()和ReportEvent();安装Windows服务须以管理员权限运行且路径加引号。

Linux systemd 和 Windows 服务怎么统一管理?
没有真正的“统一管理”,必须为每个平台单独适配启动机制。C++ 进程本身不决定自己是服务,而是由宿主环境(systemd unit / Windows service control manager)加载并监管。
关键不是“写一个跨平台服务程序”,而是让同一套业务逻辑能被两套不同的守护协议调用。核心思路是:把主业务封装成可复用的 ServiceCore 类,再分别实现 systemd_main() 和 windows_service_main() 入口。
- Linux 下用
systemd要求进程 fork 后立即退出父进程、重定向stdin/stdout/stderr到/dev/null,否则会被systemd杀掉(报错Failed to start service: Unit entered failed state) - Windows 下必须链接
Advapi32.lib,注册ServiceMain回调,并在HandlerEx中响应SERVICE_CONTROL_STOP,否则服务控制面板点“停止”无反应 - 不要在
main()里直接跑业务逻辑——它得留给平台适配层调度;否则 Windows 服务会卡在“启动中”,Linux 下则可能被systemd标记为start-limit-hit
如何让 C++ 进程正确响应 systemctrl stop 或服务管理器终止信号?
Linux 下靠捕获 SIGTERM,Windows 下靠 HandlerEx 回调。两者语义一致,但触发路径完全不同,不能混用一套信号处理逻辑。
常见错误是只处理 SIGINT(Ctrl+C),结果 systemctl stop myapp 时进程没响应,journalctl -u myapp 显示超时后被强制 KILL。
立即学习“C++免费学习笔记(深入)”;
- Linux:在
systemd_main()中调用signal(SIGTERM, signal_handler),并在 handler 里调用ServiceCore::shutdown(),然后_exit(0)(不用exit(),避免析构器干扰) - Windows:在
HandlerEx中收到SERVICE_CONTROL_STOP后,调用同一套ServiceCore::shutdown(),再用SetServiceStatus()更新状态为SERVICE_STOP_PENDING,最后返回 - 避免在 shutdown 流程中做阻塞 I/O 或锁等待——
systemd默认只等 90 秒,Windows SCM 默认等 30 秒,超时就强杀
日志输出为什么在 systemd 下看不到,但在终端里正常?
因为 systemd 不接管 stdout,除非你显式用 sd_journal_print() 或保持 stdout 指向 /dev/console(不推荐)。默认 journalctl 只收 syslog 或 sd_journal 接口写入的日志。
Windows 下同理:printf 输出不会进“事件查看器”,必须调用 ReportEvent()。
- Linux:用
sd_journal_print(LOG_INFO, "starting: %s", version.c_str()),需链接libsystemd,头文件<>systemd/sd-journal.h> - Windows:用
ReportEvent(),先RegisterEventSource(),日志类型必须是EVENTLOG_INFORMATION_TYPE等预定义值,字符串需是 UTF-16(用L"...") - 别依赖
std::cout或fprintf(stderr, ...)做服务日志——它们在 daemon 模式下通常被重定向到黑洞,且无法打时间戳和优先级
Windows 服务安装失败提示 “Error 5: Access is denied” 怎么办?
这不是代码问题,是权限问题。Windows 服务安装命令 sc create 或 InstallUtil.exe 必须以管理员身份运行,哪怕当前用户是 Administrator 也不行——必须右键“以管理员身份运行”命令行。
另一个常见原因是路径含空格但没加引号,比如 sc create MySvc binPath= C:\my app\svc.exe 会解析成两个参数,报错 1053: The service did not respond to the start or control request in a timely fashion。
- 正确写法:
sc create MySvc binPath= "C:\my app\svc.exe" start= auto(注意binPath=后有空格,引号必须紧贴等号后) - 检查二进制是否启用了
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER(误标为驱动),会导致sc create静默失败 - 安装后用
sc qc MySvc确认BINARY_PATH_NAME是否完整、无乱码,START_TYPE是否为DEMAND_START或AUTO_START
跨平台服务最难的不是代码,是理解两套守护模型对进程生命周期的约束:systemd 管的是 cgroup + socket + exec,Windows SCM 管的是 session + token + control pipe。写错一个状态上报或漏一个信号处理,就会卡在“启动中”或“停止中”不动——这种问题往往没报错,只能看日志或 proc 文件系统。










