本地 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 操作系统上可用,在 Windows 操作系统中是默认后端。另外两个通常都可在 Unix 操作系统上使用。以下表提供了自 Qt 6.6 以来典型可用性的概述

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

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

注意:2 一些与 GCC 兼容的 Windows C 运行时提供了 POSIX 兼容的共享内存支持,但这很少见。使用 Microsoft 编译器时总 absent。

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

QNativeIpcKey 还为之前 Qt 应用程序的兼容性提供了支持。以下部分详细说明了后端限制、字符串键的内容以及兼容性。

跨平台安全键格式

QNativeIpcKey::setNativeKey() 和 QNativeIpcKey::nativeKey() 处理低级本地键,这可以与本地 API 一起使用,并与其他非 Qt 进程共享(见以下 API)。此格式通常不在跨平台之间可用,因此 QSharedMemory 和 QSystemSemaphore 都提供了一个将跨平台标识符字符串转换为本地密钥的函数:QSharedMemory::platformSafeKey() 和 QSystemSemaphore::platformSafeKey()。

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

苹果沙盒限制:如果应用程序在苹果操作系统中沙盒内部运行,则键必须采用非常具体的格式:<应用程序组标识符>/<自定义标识符>。所有通过苹果应用商店分发的应用程序都隐含了沙盒。有关更多信息,包括如何获取应用程序组标识符,请参阅苹果的文档在此此处

本地键格式

本节详细介绍支持的后端本地键的格式。

POSIX实时

本地键类似于文件名,可以包含文件名中允许的任何字符,除了斜杠。POSIX要求键名中的第一个字符是斜杠,但未确定是否允许额外的斜杠。在大多数操作系统上,键的长度与文件名相同,但在苹果操作系统中受限为32个字符(包括第一个斜杠和终止的空字符,因此只能使用30个可用字符)。

以下是一些本地POSIX键的示例:“/myapp”,“/org.example.myapp”,“/org.example.myapp-12345”。

QSharedMemory::platformSafeKey()和QSystemSemaphore::platformSafeKey()只是简单地在前面添加斜杠。在苹果操作系统中,它们还将结果截断为可用的大小。

Windows

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

以下是一些本地Windows键的示例:“myapp”,“org.example.myapp”,“org.example.myapp-12345”。

QSharedMemory::platformSafeKey()和QSystemSemaphore::platformSafeKey()分别插入一个前缀以区分共享内存和系统信号量。

X/Open系统接口(XSI)/系统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之前,Qt从未清理POSIX实时对象,除非是在QNX上。

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

Qt类管理两种资源:键所指向的文件以及对象本身。 QSharedMemory 协同管理对象:最后一个附加负责删除对象本身,然后删除密钥文件。 QSystemSemaphore 只有在传递 QSystemSemaphore::Create 时才会删除对象;此外,如果它创建了密钥文件,也会将其删除。

从Qt 6.6开始,可以请求这两个类都不进行清理。

Windows

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

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

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

Qt 6.6保留了配置脚本选项,但它不再控制后端的可用性。相反,它更改了QNativeIpcKey::legacyDefaultTypeForOs() 将返回的内容。必须使用此密钥类型来确保互操作性的应用程序必须独家使用此键类型。

QSharedMemoryQSystemSemaphore 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)/系统V

不要使用现有文件为QSharedMemory键,因为旧版本Qt应用程序可能尝试删除它。相反,让QSharedMemory自行创建。

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

与非Qt应用程序的互操作性是可能的,但有一些限制

  • 创建共享内存段的操作不得相互冲突
  • QSharedMemory不支持锁定段

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

QSharedMemory将整个段映射到内存。非Qt应用程序可以选择只将其子集映射到内存,不会有任何不良影响。

POSIX实时

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

这两个函数都带有一个name参数,该参数是QNativeIpcKey::nativeKey()的结果,采用QFile::encodeName() / QFile::decodeName()进行文件名编码。

Windows

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

这些函数的lpName参数是QNativeIpcKey::nativeKey()的结果,无需变换。

如果外部应用程序使用这些函数的非Unicode版本(以"A"结尾),可以使用QString将这些名称转换为8位,并反过来。

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

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

这两个函数的key参数是当将QNativeIpcKey::nativeKey()获取的文件名与id为81或0x51(ASCII大写字母'Q')一起传递给ftok()函数时得到的结果。

System V信号量对象可能包含多个信号量,但QSystemSemaphore只使用第一个(对于sem_num的值为0)。

如果QSharedMemoryQSystemSemaphore是最后一个附件,它们将默认使用IPC_RMID操作自动从shmctl()semctl()中删除对象。

© 2024 Qt公司有限公司。此处包含的文档贡献属于其各自所有者的版权。此处提供的文档根据Free Software Foundation发布的GNU自由文档许可证版本1.3的条款进行许可。Qt及其标志是芬兰及其他在世界各地的Qt公司的商标。其他所有商标均属于其各自所有者。