You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

systemd服务启动C++程序无法创建POSIX信号量问题排查

问题描述

我有一个用于启动C++应用的systemd服务文件,应用通过以下代码创建信号量:

access_sem = sem_open("/sync_nvme_access_sem", O_CREAT, 0666);
if (access_sem == SEM_FAILED) {
       std::cerr << "Cannot create the semaphore" << std::endl;
       return false;
}

对应的systemd服务文件内容如下:

[Unit]
Description=This is used to start the server
Requires=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/server
ExecStop=/bin/sh /usr/bin/stop-server.sh
WorkingDirectory=/
Restart=on-failure

[Install]
WantedBy=multi-user.target

单独以root身份运行该应用时一切正常,但通过systemd守护进程启动时无法创建信号量,请问原因是什么?

问题原因及解决办法

核心原因1:sem_open调用参数不完整

POSIX标准明确要求,当使用O_CREAT标志调用sem_open时,必须传入四个参数:信号量名称、标志位、权限模式、初始值。你的代码只传入了前三个参数,缺少信号量初始值这一必填项,属于未定义行为。

单独以root运行时能正常工作是因为:之前已经创建过同名信号量,此时sem_open实际是打开已存在的信号量而非创建,参数不全的问题被掩盖;但systemd启动时因为环境隔离,无法访问全局已存在的信号量,必须尝试创建,此时参数缺失直接导致调用失败。

核心原因2:systemd默认的私有临时目录隔离

systemd服务默认启用PrivateTmp=true,会为服务分配独立的临时文件系统(包括POSIX命名信号量实际存储的/dev/shm目录)。这会导致:

  • 服务无法访问全局/dev/shm中已存在的信号量
  • 即使修复参数问题,创建的信号量也仅存在于服务的私有临时空间内,无法与其他进程共享

解决步骤

  1. 修复sem_open调用:补充信号量初始值参数,例如设置初始值为1:
access_sem = sem_open("/sync_nvme_access_sem", O_CREAT, 0666, 1);
  1. 调整systemd服务配置:在[Service]段添加PrivateTmp=false,关闭私有临时目录隔离,让服务能访问全局的/dev/shm:
[Service]
Type=simple
ExecStart=/usr/bin/server
ExecStop=/bin/sh /usr/bin/stop-server.sh
WorkingDirectory=/
Restart=on-failure
PrivateTmp=false
  1. 重新加载systemd配置并重启服务:
systemctl daemon-reload
systemctl restart your-service-name.service

内容的提问来源于stack exchange,提问作者PMDP3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 20:33:35