Qt provides two techniques to share memory with other processes in the same system: QSharedMemory and memory-mapped files using QFile. Memory that is shared with other processes is often referred to as a "segment", and although it may have been implemented as specific segments on processors with segmented memory models in the past, this is not the case in any modern operating system. Shared memory segments are simply regions of memory that the operating system will ensure are available to all processes participating.
Note: The address at which the segment is located in memory will almost always be different for each process that is participating in the sharing. Therefore, applications must take care to share only position-independent data, such as primitive C++ types or arrays of such types.
QSharedMemory provides a simple API to create a shared memory segment of a given size or attach to one that was created by another process. Additionally, it provides a pair of methods to lock and unlock the whole segment, using an internal QSystemSemaphore.
Shared memory segments and system semaphores are globally identified in the system through a "key", which in Qt is represented by the QNativeIpcKey class. Additionally, depending on the OS, Qt may support multiple different backends for sharing memory; see the Native IPC Keys documentation for more information and limitations.
QSharedMemory is designed to share memory only within the same privilege level (that is, not with untrusted other processes, such as those started by other users). For backends that support it, QSharedMemory will create segments such that only processes with the same privilege level can attach.
Most files can be mapped to memory using QFile::map() and, if the MapPrivateOption option is not specified, any writes to the mapped segment will be observed by all other processes that have mapped the same file. Exceptions to files that can be mapped to memory include remote files found in network shares or those located in certain filesystems. Even if the operating system does allow mapping remote files to memory, I/O operations on the file will likely be cached and delayed, thus making true memory sharing impossible.
This solution has the major advantages of being independent of any backend API and of being simpler to interoperate with from non-Qt applications. Since QTemporaryFile is a QFile, applications can use that class to achieve clean-up semantics and to create unique shared memory segments too.
To achieve locking of the shared memory segment, applications will need to deploy their own mechanisms. One way may be to use QLockFile. Another and less costly solution is to use QBasicAtomicInteger or
std::atomic in a pre-determined offset in the segment itself. Higher-level locking primitives may be available on some operating systems; for example, on Linux, applications can set the "pshared" flag in the mutex attribute passed to
pthread_mutex_create() to indicate that the mutex resides in a shared memory segment.
Be aware that the operating system will likely attempt to commit to permanent storage any writes made to the shared memory. This may be desired or it may be a performance penalty if the file itself was meant to be temporary. In that case, applications should locate a RAM-backed filesystem, such as
tmpfs on Linux (see QStorageInfo::fileSystemType()), or pass a flag to the native file-opening function to inform the OS to avoid committing the contents to storage.
It is possible to use file-backed shared memory to communicate with untrusted processes, in which case the application should exercise great care. The files may be truncated/shrunk and cause applications accessing memory beyond the file's size to crash.
On modern Linux systems, while the
/tmp directory is often a
tmpfs mount point, that is not a requirement. However, the
/dev/shm directory is required to be a
tmpfs and exists for the very purpose of sharing memory. Do note that it is world-readable and writable (like
/var/tmp), so applications must be careful of the contents revealed there. Another alternative is to use the XDG Runtime Directory (see QStandardPaths::writableLocation() and QStandardPaths::RuntimeLocation), which on Linux systems using systemd is a user-specific
An even more secure solution is to create a "memfd" using
memfd_create(2) and use interprocess communication to pass the file descriptor, like QDBusUnixFileDescriptor or by letting the child process of a QProcess inherit it. "memfds" can also be sealed against being shrunk, so they are safe to be used when communicating with processes with a different privilege level.
FreeBSD also has
memfd_create(2) and can pass file descriptors to other processes using the same techniques as Linux. It does not have temporary filesystems mounted by default.
On Windows, the application can request the operating system avoid saving the file's contents on permanent storage. This request is performed by passing the
FILE_ATTRIBUTE_TEMPORARY flag in the
dwFlagsAndAttributes parameter to the
CreateFile Win32 function, the
_O_SHORT_LIVED flag to
_open() low-level function, or by including the modifier "T" to the
fopen() C runtime function.
There's also a flag to inform the operating system to delete the file when the last handle to it is closed (
_O_TEMPORARY, and the "D" modifier), but do note that all processes attempting to open the file must agree on using this flag or not using it. A mismatch will likely cause a sharing violation and failure to open the file.
© 2023 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.