QSharedMemory/doc: update docs to be more modern

- We don't support HPUX any more, so no need to talk about it
- Clarify that it's the QSharedMemory destructor that releases the
  resources on Unix systems
- Explain the System V and POSIX backends and how to detect them
- Add a note about Android not being supported.
- Add a section about using QFile for shared memory

Change-Id: I413ea647c2a5453b8307fffd17174c8083416529
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2022-09-22 14:38:22 -07:00 committed by Tor Arne Vestbø
parent 47ab723d82
commit 909112fa52

View File

@ -105,22 +105,34 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\li Unix: QSharedMemory "owns" the shared memory segment. When the
last thread or process that has an instance of QSharedMemory
attached to a particular shared memory segment detaches from the
segment by destroying its instance of QSharedMemory, the Unix kernel
release the shared memory segment. But if that last thread or
segment by destroying its instance of QSharedMemory, the destructor
releases the shared memory segment. But if that last thread or
process crashes without running the QSharedMemory destructor, the
shared memory segment survives the crash.
\li HP-UX: Only one attach to a shared memory segment is allowed per
process. This means that QSharedMemory should not be used across
multiple threads in the same process in HP-UX.
\li Unix: QSharedMemory can be implemented by one of two different
backends, selected at Qt build time: System V or POSIX. Qt defaults to
using the System V API if it is available, and POSIX if not. These two
backends do not interoperate, so two applications must ensure they use the
same one, even if the native key (see setNativeKey()) is the same.
\li Apple platforms: Sandboxed applications (including apps
shipped through the Apple App Store) require the use of POSIX
shared memory (instead of System V shared memory), which adds
a number of limitations, including:
The POSIX backend can be explicitly selected using the
\c{-feature-ipc_posix} option to the Qt configure script. If it is enabled,
the \c{QT_POSIX_IPC} macro will be defined.
\li Sandboxed applications on Apple platforms (including apps
shipped through the Apple App Store): This environment requires
the use of POSIX shared memory (instead of System V shared memory).
Qt for iOS is built with support for POSIX shared memory out of the box.
However, Qt for \macos builds (including those from the Qt installer) default
to System V, making them unsuitable for App Store submission if QSharedMemory
is needed. See above for instructions to explicitly select the POSIX backend
when building Qt.
In addition, in a sandboxed environment, the following caveats apply:
\list
\li The key must be in the form \c {<application group identifier>/<custom identifier>},
as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
{here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
@ -143,11 +155,7 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\endlist
Qt for iOS comes with support for POSIX shared memory out of the box.
With Qt for \macos an additional configure flag must be added when
building Qt to enable the feature. To enable the feature pass
\c {-feature-ipc_posix} Note that the pre-built Qt libraries for
\macos available through the Qt installer do not include this feature.
\li Android: QSharedMemory is not supported.
\endlist
@ -162,9 +170,79 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\warning QSharedMemory changes the key in a Qt-specific way, unless otherwise
specified. Interoperation with non-Qt applications is achieved by first creating
a default shared memory with QSharedMemory() and then setting a native key with
setNativeKey(). When using native keys, shared memory is not protected against
multiple accesses on it (for example, unable to lock()) and a user-defined mechanism
setNativeKey(), after ensuring they use the same low-level API (System V or
POSIX). When using native keys, shared memory is not protected against multiple
accesses on it (for example, unable to lock()) and a user-defined mechanism
should be used to achieve such protection.
\section2 Alternative: Memory-Mapped File
Another way to share memory between processes is by opening the same file
using \l QFile and mapping it into memory using QFile::map() (without
specifying the QFileDevice::MapPrivateOption option). Any writes to the
mapped segment will be observed by all other processes that have mapped the
same file. This solution has the major advantages of being independent of the
backend API and of being simpler to interoperate with from non-Qt
applications. And since \l{QTemporaryFile} is a \l{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. This can be achieved by using \l
QBasicAtomicInteger or \c{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, \c{pthread_mutex_create()} can be
passed a flag to indicate that the mutex resides in a shared memory segment.
A major drawback of using file-backed shared memory is that the operating
system will attempt to write the data to permanent storage, possibly causing
noticeable performance penalties. To avoid this, applications should locate a
RAM-backed filesystem, such as \c{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.
File-backed shared memory must be used with care if another process
participating is untrusted. The files may be truncated/shrunk and cause
applications accessing memory beyond the file's size to crash.
\section3 Linux hints on memory-mapped files
On modern Linux systems, while the \c{/tmp} directory is often a \c{tmpfs}
mount point, that is not a requirement. However, the \c{/dev/shm} directory
is required to be a \c{tmpfs} and exists for this very purpose. Do note that
it is world-readable and writable (like \c{/tmp} and \c{/var/tmp}), so one
must be careful of the contents revealed there. Another alternative is to use
the XDG Runtime Directory (see QStandardPaths::writableLocation() and
\l{QStandardPaths::RuntimeLocation}), which on Linux systems using systemd is
a user-specific \c{tmpfs}.
An even more secure solution is to create a "memfd" using \c{memfd_create(2)}
and use interprocess communication to pass the file descriptor, like
\l{QDBusUnixFileDescriptor} or by letting the child process of a \l{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.
\section3 FreeBSD hints on memory-mapped files
FreeBSD also has \c{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.
\section3 Windows hints on memory-mapped files
On Windows, the application can request the operating system avoid committing
the file's contents to permanent storage. This request is performed by
passing the \c{FILE_ATTRIBUTE_TEMPORARY} flag in the \c{dwFlagsAndAttributes}
\c{CreateFile} Win32 function, the \c{_O_SHORT_LIVED} flag to \c{_open()}
low-level function, or by including the modifier "T" to the \c{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 (\c{FILE_FLAG_DELETE_ON_CLOSE},
\c{_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.
*/
/*!