Shared Memory
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.
Sharing memory using QSharedMemory
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.
Sharing memory via memory-mapped files
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.
Linux hints on memory-mapped files
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 /tmp
and /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 tmpfs
.
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 hints on memory-mapped files
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.
Windows hints on memory-mapped files
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 (FILE_FLAG_DELETE_ON_CLOSE
, _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.