本页内容

本机进程间通信(IPC)密钥

QSharedMemory 和QSystemSemaphore 类使用一种称为“键”的系统级标识符来标识其资源。低级键值以及键类型在Qt中通过QNativeIpcKey 类进行封装。该类还提供了通过QNativeIpcKey::toString()和QNativeIpcKey::fromString()方法与其他进程交换键的适当方式。

Qt 目前为这两个类支持三种不同的后端,它们与QNativeIpcKey::Type 枚举中的可用值相对应。

  • POSIX 实时扩展(IEEE 1003.1b、POSIX.1b)
  • X/Open 系统接口 (XSI) 或 System V (SVr4),尽管现在也属于 POSIX 的一部分
  • Windows 原语

顾名思义,Windows 原语仅在 Windows 操作系统上可用,且在该系统中作为默认后端。其余两种后端通常在 Unix 操作系统上均可使用。下表概述了自 Qt 6.6 以来各后端的典型可用性:

操作系统POSIXSystem VWindows
Android
INTEGRITY
QNX是
macOS是通常 (1)
其他苹果操作系统是
其他 Unix 系统是是
Windows很少 (2)是

注:1 处于沙盒环境中的 macOS 应用程序(包括通过 Apple App Store 分发的所有应用程序)可能无法使用 System V 对象。

注:2 Windows 上某些与 GCC 兼容的 C 运行时环境提供与 POSIX 兼容的共享内存支持,但这种情况很少见。Microsoft 编译器始终不支持此功能。

要确定是否支持给定的键类型,应用程序应调用 QSharedMemory::isKeyTypeSupported() 和 QSystemSemaphore::isKeyTypeSupported()。

QNativeIpcKey 还提供了与该功能推出之前 Qt 应用程序的兼容性支持。以下各节详细说明了后端功能的限制、字符串键本身的内容以及兼容性问题。

跨平台安全的键格式

QNativeIpcKey::setNativeKey() 和QNativeIpcKey::nativeKey() 处理低级本机密钥,该密钥可与本机 API 配合使用,并可与其他非 Qt 进程共享(API 详见下文)。 这种格式通常不具备跨平台性,因此QSharedMemory 和QSystemSemaphore 都提供了一个函数,用于将跨平台标识符字符串转换为本机键:QSharedMemory::platformSafeKey() 和 QSystemSemaphore::platformSafeKey()。

在大多数平台上,跨平台密钥的长度与文件名长度相同,但在 Apple 平台上受到严格限制,仅允许使用 30 字节(如果使用 US-ASCII 范围外的字符,请注意 UTF-8 编码)。 密钥的格式也类似于文件路径的组成部分,这意味着它不应包含文件名中不允许的任何字符,特别是用于分隔路径组成部分的字符(斜杠和反斜杠),但 Apple 操作系统上的沙盒应用程序除外。 以下是跨平台密钥的良好示例:“myapp”、“org.example.myapp”、“org.example.myapp-12345”。 请注意,防止密钥长度超标并确保密钥包含各平台允许的字符是调用方的责任。Qt 会静默截断过长的密钥。

Apple 沙盒限制:如果应用程序在 Apple 操作系统的沙盒内运行,密钥必须采用非常特定的格式:<application group identifier>/<custom identifier> 。通过 Apple App Store 分发的所有应用程序均默认处于沙盒环境中。有关更多信息(包括如何获取应用程序的组标识符),请参阅 Apple 的文档( 此处和此处)。

原生密钥格式

本节详细说明了受支持后端原生密钥的格式。

POSIX 实时

原生密钥类似于文件名,可以包含文件名中允许的任何字符,但斜杠除外。POSIX 要求密钥名称的首字符必须是斜杠,但未明确规定是否允许出现额外的斜杠。 在大多数操作系统上,密钥长度与文件名相同,但在 Apple 操作系统上限制为 32 个字符(这包括第一个斜杠和结尾的空字符,因此实际可用字符仅为 30 个)。

以下是原生 POSIX 键名的良好示例:“/myapp”、“/org.example.myapp”、“/org.example.myapp-12345”。

QSharedMemory::platformSafeKey() 和 QSystemSemaphore::platformSafeKey() 仅在键值前添加斜杠。在 Apple 操作系统上,它们还会将结果截断至可用长度。

Windows

Windows 键类型是 NT内核对象名称,长度最多可达MAX_PATH (260)个字符。它们看起来像相对路径(即不以反斜杠或驱动器字母开头),但与 Windows 上的文件名不同,它们区分大小写。

以下是 Windows 本机键的良好示例:“myapp”、“org.example.myapp”、“org.example.myapp-12345”。

QSharedMemory::platformSafeKey() 和 QSystemSemaphore::platformSafeKey() 会分别插入一个前缀,以消除共享内存和系统信号量之间的歧义。

X/Open 系统接口 (XSI) / System V

System V 键采用系统中文件名的形式,因此具有与文件路径完全相同的限制。如果创建对象时该文件不存在,QSharedMemory 和QSystemSemaphore 都会创建该文件。 如果禁用了自动删除功能,该键还可被QSharedMemory 和QSystemSemaphore 共享而不会产生冲突,并且可以是任何现有的文件(例如,可以是进程可执行文件本身,参见QCoreApplication::applicationFilePath())。路径应为绝对路径,以避免因当前目录不同而导致的错误。

QSharedMemory::platformSafeKey() 和 QSystemSemaphore::platformSafeKey() 始终返回绝对路径。如果输入已经是绝对路径,它们将直接返回该输入;否则,它们会在前面添加一个合适的路径,该路径通常是应用程序有权创建文件的位置。

所有权

共享内存和系统信号量对象在使用前需要先创建,分别可通过调用QSharedMemory::create()或向构造函数传递QSystemSemaphore::Create 来实现。

在 Unix 系统上,创建该对象的 Qt 类将负责清理该对象。 因此,如果包含该 C++ 对象的应用程序以非正常方式退出(例如崩溃、调用qFatal() 等),该对象可能会被遗留下来。如果发生这种情况,应用程序可能无法再次创建该对象,而应连接到现有的对象。例如,对于QSharedMemory :

if (!shm.create(4096) && shm.error() == QSharedMemory::AlreadyExists)
    shm.attach();

重新连接到一个QSystemSemaphore 可能并不明智,因为其中的令牌计数器可能处于未知状态,从而可能导致死锁。

POSIX 实时

POSIX 实时对象的所有权模式参照文件设计,即无论是否有进程使用,它们都独立存在。Qt 无法判断对象是否仍在使用中,因此自动清理操作仍会将其移除,这将导致无法再次连接到同一对象,但不会影响现有的连接。

在 Qt 6.6 之前,除 QNX 平台外,Qt 从未清理过 POSIX 实时对象。

X/Open 系统接口 (XSI) / System V

Qt 类管理着两项资源:键所引用的文件以及对象本身。QSharedMemory 以协作方式管理对象:最后一个连接负责先移除对象本身,然后移除键文件。QSystemSemaphore 仅当其被传递了QSystemSemaphore::Create 时才会移除对象;此外,如果它创建了键文件,也会一并移除该文件。

自 Qt 6.6 起,可以要求上述任一类不进行清理。

Windows

操作系统拥有该对象,并在指向该对象的最后一个句柄关闭后进行清理。

与旧版 Qt 应用程序的互操作性

QNativeIpcKey 类是在Qt 6.6中引入的。在此版本之前,QSharedMemory 和QSystemSemaphore 后端是在Qt自身构建时确定的。对于Windows系统,它始终是Windows后端。 对于 Unix 系统,如果配置脚本确定 System V 后端可用,则默认使用该后端;若不可用,则回退到 POSIX 后端。可通过在 Qt 配置脚本中使用-feature-ipc_posix 选项显式选择 POSIX 后端;若启用该选项,则会定义QT_POSIX_IPC 宏。

Qt 6.6 保留了该 configure 脚本选项,但它不再控制后端的可用性。取而代之的是,它会改变QNativeIpcKey::legacyDefaultTypeForOs() 的返回值。需要保持兼容性的应用程序必须仅使用此键类型,以确保互操作性。

QSharedMemory 和QSystemSemaphore 中的 API 曾包含跨平台密钥的概念,该概念现已弃用,建议改用 QSharedMemory::legacyNativeKey() 和 QSystemSemaphore::legacyNativeKey()。这两个函数生成的本机密钥与先前版本中已弃用的函数生成的密钥相同。 例如,如果旧代码如下:

QSharedMemory shm("org.example.myapplication");
QSystemSemaphore sem("org.example.myapplication");

则可更新为:

QSharedMemory shm(QSharedMemory::legacyNativeKey("org.example.myapplication"));
QSystemSemaphore sem(QSystemSemaphore::legacyNativeKey("org.example.myapplication"));

如果两个应用程序之间交换了本机密钥,则无需更新如下代码:

QSharedMemory shm;
shm.setNativeKey(key);

不过,如果旧应用程序确实接受原生密钥,新应用程序可以选择使用platformSafeKey() ,并将第二个参数设置为QNativeIpcKey::legacyDefaultTypeForOs()。

X/Open 系统接口 (XSI) / System V

切勿将现有文件用作 `QSharedMemory ` 密钥,因为旧版 Qt 应用程序可能会尝试将其删除。相反,应让 `QSharedMemory ` 自动创建该文件。

与非 Qt 应用程序的互操作性

与非 Qt 应用程序的互操作性是可行的,但存在一些限制:

  • 创建共享内存段时必须避免竞争
  • QSharedMemory 不支持对段进行加锁

与非 Qt 应用程序的通信必须始终通过本机密钥进行。

QSharedMemory 始终将整个段映射到内存中。非 Qt 应用程序可选择仅将其中一部分映射到内存,且不会产生不良影响。

POSIX 实时

可以使用shm_open()打开 POSIX 共享内存,使用sem_open() 打开 POSIX 系统信号量。

这两个函数都接受一个name 参数,该参数是QNativeIpcKey::nativeKey()的结果,并使用QFile::encodeName() /QFile::decodeName()对文件名进行了编码。

Windows

Windows 共享内存对象可使用CreateFileMappingW打开,Windows 系统信号量对象可使用CreateSemaphoreW 打开。尽管这两个函数的名称都以“Create”开头,但它们能够附加到现有对象上。

这些函数的lpName 参数是QNativeIpcKey::nativeKey()的返回值,未经过转换。

如果外部应用程序使用这些函数的非 Unicode 版本(以“A”结尾),则可以使用QString 将名称在 8 位和 Unicode 之间进行转换。

X/Open 系统接口 (XSI) / System V

可以使用shmget()获取 System V 共享内存,使用semget() 获取 System V 系统信号量。

这两个函数的key 参数是ftok()函数的结果,该函数接受从QNativeIpcKey::nativeKey() 获得的文件名,其id 为 81 或 0x51(ASCII 大写字母“Q”)。

System V 信号量对象可能包含多个信号量,但QSystemSemaphore 仅使用第一个(对于sem_num 而言,编号为 0)。

QSharedMemory 和QSystemSemaphore 默认都会在作为最后一个连接时,分别通过IPC_RMID 操作将对象移至shmctl() 和semctl() 。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.