Native IPC Keys
The QSharedMemory and QSystemSemaphore classes identify their resource using a system-wide identifier known as a "key". The low-level key value as well as the key type are encapsulated in Qt using the QNativeIpcKey class. That class also provides the proper means of exchanging the key with other processes, by way of QNativeIpcKey::toString() and QNativeIpcKey::fromString().
Qt currently supports three distinct backends for those two classes, which match the values available in the QNativeIpcKey::Type enumeration.
- POSIX Realtime extensions (IEEE 1003.1b, POSIX.1b)
- X/Open System Interfaces (XSI) or System V (SVr4), though also now part of POSIX
- Windows primitives
As the name indicates, the Windows primitives are only available on the Windows operating system, where they are the default backend. The other two are usually both available on Unix operating systems. The following table provides an overview of typical availability since Qt 6.6:
Operating system | POSIX | System V | Windows |
---|---|---|---|
Android | |||
INTEGRITY | |||
QNX | Yes | ||
macOS | Yes | Usually (1) | |
Other Apple OSes | Yes | ||
Other Unix systems | Yes | Yes | |
Windows | Rarely (2) | Yes |
Note: 1 Sandboxed macOS applications, which include all applications distributed via the Apple App Store, may not use System V objects.
Note: 2 Some GCC-compatible C runtimes on Windows provide POSIX-compatible shared memory support, but this is rare. It is always absent with the Microsoft compiler.
To determine whether a given key type is supported, applications should call QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
QNativeIpcKey also provides support for compatibility with Qt applications prior to its introduction. The following sections detail the limitations of the backends, the contents of the string keys themselves, and compatibility.
Cross-platform safe key format
QNativeIpcKey::setNativeKey() and QNativeIpcKey::nativeKey() handle the low-level native key, which may be used with the native APIs and shared with other, non-Qt processes (see below for the API). This format is not usually cross-platform, so both QSharedMemory and QSystemSemaphore provide a function to translate a cross-platform identifier string to the native key: QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey().
The length of the cross-platform key on most platforms is the same as that of a file name, but is severely limited on Apple platforms to only 30 usable bytes (be mindful of UTF-8 encoding if using characters outside the US-ASCII range). The format of the key is also similar to that of a file path component, meaning it should not contain any characters not allowed in file names, in particular those that separate path components (slash and backslash), with the exception of sandboxed applications on Apple operating systems. The following are good examples of cross-platform keys: "myapp", "org.example.myapp", "org.example.myapp-12345". Note that it is up to the caller to prevent oversized keys, and to ensure that the key contains legal characters on the respective platform. Qt will silently truncate keys that are too long.
Apple sandbox limitations: if the application is running inside of a sandbox in an Apple operating system, the key must be in a very specific format: <application group identifier>/<custom identifier>
. Sandboxing is implied for all applications distributed through the Apple App Store. See Apple's documentation here and here for more information, including how to obtain the application's group identifier.
Native key format
This section details the format of the native keys of the supported backends.
POSIX Realtime
Native keys resemble file names and may contain any character that file names do, except for a slash. POSIX requires the first character in the key name to be a slash and leaves undetermined whether any additional slashes are permitted. On most operating systems, the key length is the same as a file name, but it is limited to 32 characters on Apple operating systems (this includes the first slash and the terminating null, so only 30 usable characters are possible).
The following are good examples of native POSIX keys: "/myapp", "/org.example.myapp", "/org.example.myapp-12345".
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey() simply prepend the slash. On Apple operating systems, they also truncate the result to the available size.
Windows
Windows key types are NT kernel object names and may be up to MAX_PATH
(260) characters in length. They look like relative paths (that is, they don't start with a backslash or a drive letter), but unlike file names on Windows, they are case-sensitive.
The following are good examples of native Windows keys: "myapp", "org.example.myapp", "org.example.myapp-12345".
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey() insert a prefix to disambiguate shared memory and system semaphores, respectively.
X/Open System Interfaces (XSI) / System V
System V keys take the form of the name of a file in the system, and thus have the exact same limitations as file paths do. Both QSharedMemory and QSystemSemaphore will create this file if it does not exist when creating the object. If auto-removal is disabled, it may also be shared between QSharedMemory and QSystemSemaphore without conflict and can be any extant file (for example, it can be the process executable itself, see QCoreApplication::applicationFilePath()). The path should be an absolute one to avoid mistakes due to different current directories.
QSharedMemory::platformSafeKey() and QSystemSemaphore::platformSafeKey() always return an absolute path. If the input was already absolute, they will return their input unchanged. Otherwise, they will prepend a suitable path where the application usually has permission to create files in.
Ownership
Shared memory and system semaphore objects need to be created before use, which is accomplished with QSharedMemory::create() or by passing QSystemSemaphore::Create to the constructor, respectively.
On Unix systems, the Qt classes that created the object will be responsible for cleaning up the object in question. Therefore, if the application with that C++ object exits uncleanly (a crash, qFatal(), etc.), the object may be left behind. If that happens, applications may fail to create the object again and should instead attach to an existing one. For example, for QSharedMemory:
if (!shm.create(4096) && shm.error() == QSharedMemory::AlreadyExists) shm.attach();
Re-attaching to a QSystemSemaphore is probably unwise, as the token counter in it is probably in an unknown state and therefore may lead to deadlocks.
POSIX Realtime
POSIX Realtime object ownership is patterned after files, in the sense that they exist independent of any process using them or not. Qt is unable to determine if the object is still in use, so auto-removal will remove it even then, which will make attaching to the same object impossible but otherwise not affecting existing attachments.
Prior to Qt 6.6, Qt never cleaned up POSIX Realtime objects, except on QNX.
X/Open System Interfaces (XSI) / System V
There are two resources managed by the Qt classes: the file the key refers to and the object itself. QSharedMemory manages the object cooperatively: the last attachment is responsible for removing the object itself and then removing the key file. QSystemSemaphore will remove the object if and only if it was passed QSystemSemaphore::Create; additionally, if it created the key file, it will remove that too.
Since Qt 6.6, it is possible to ask either class not to clean up.
Windows
The operating system owns the object and will clean up after the last handle to the object is closed.
Interoperability with old Qt applications
The QNativeIpcKey class was introduced in Qt 6.6. Prior to this version, QSharedMemory and QSystemSemaphore backends were determined at the time of Qt's own build. For Windows systems, it was always the Windows backend. For Unix systems, it defaulted to the System V backend if the configuration script determined it was available. If it was not available, it fell back to the POSIX one. The POSIX backend could be explicitly selected using the -feature-ipc_posix
option to the Qt configure script; if it was enabled, the QT_POSIX_IPC
macro would be defined.
Qt 6.6 retains the configure script option but it no longer controls the availability of the backends. Instead, it changes what QNativeIpcKey::legacyDefaultTypeForOs() will return. Applications that need to retain compatibility must use this key type exclusively to guarantee interoperability.
The API in both QSharedMemory and QSystemSemaphore had the concept of a cross-platform key, which is now deprecated in favor of using QSharedMemory::legacyNativeKey() and QSystemSemaphore::legacyNativeKey(). Those two functions produce the same native key as the deprecated functions did in prior versions. If the old code was for example:
QSharedMemory shm("org.example.myapplication"); QSystemSemaphore sem("org.example.myapplication");
It can be updated to be:
QSharedMemory shm(QSharedMemory::legacyNativeKey("org.example.myapplication")); QSystemSemaphore sem(QSystemSemaphore::legacyNativeKey("org.example.myapplication"));
If the two applications exchanged native keys, there is no need to update code such as:
QSharedMemory shm; shm.setNativeKey(key);
Though if the older application did accept a native key, the new one may opt to use platformSafeKey()
with a second argument of QNativeIpcKey::legacyDefaultTypeForOs().
X/Open System Interfaces (XSI) / System V
Never use existing files for QSharedMemory keys, as the old Qt application may attempt to remove it. Instead, let QSharedMemory create it.
Interoperability with non-Qt applications
Interoperability with non-Qt applications is possible, with some limitations:
- Creation of shared memory segments must not race
- QSharedMemory support for locking the segment is unavailable
Communication with non-Qt applications must always be through the native key.
QSharedMemory always maps the entire segment to memory. The non-Qt application may choose to only map a subset of it to memory, with no ill effects.
POSIX Realtime
POSIX shared memory can be opened using shm_open() and POSIX system semaphores can be opened using sem_open().
Both of those functions take a name
parameter that is the result of QNativeIpcKey::nativeKey(), encoded for file names using QFile::encodeName() / QFile::decodeName().
Windows
Windows shared memory objects can be opened using CreateFileMappingW and Windows system semaphore objects can be opened using CreateSemaphoreW. Despite the name of both functions starting with "Create", they are able to attach to existing objects.
The lpName
parameter to those functions is the result of QNativeIpcKey::nativeKey(), without transformation.
If the foreign application uses the non-Unicode version of those functions (ending in "A"), the name can be converted to and from 8-bit using QString.
X/Open System Interfaces (XSI) / System V
System V shared memory can be obtained using shmget() and System V system semaphores can be obtained using semget().
The key
parameter to either of those functions is the result of the ftok() function when passed the file name obtained from QNativeIpcKey::nativeKey() with an id
of 81 or 0x51 (the ASCII capital letter 'Q').
System V semaphore objects may contain multiple semaphores, but QSystemSemaphore only uses the first one (number 0 for sem_num
).
Both QSharedMemory and QSystemSemaphore default to removing the object using the IPC_RMID
operation to shmctl()
and semctl()
respectively if they are the last attachment.