QSharedMemory 类

QSharedMemory 类提供对共享内存段的访问。 更多...

头: #include <QSharedMemory>
CMake: find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake: QT += core
继承: QObject

公共类型

enum AccessMode { ReadOnly, ReadWrite }
enum SharedMemoryError { NoError, PermissionDenied, InvalidSize, KeyError, AlreadyExists, …, UnknownError }

公共函数

QSharedMemory (const QString & key , QObject * parent = nullptr)
QSharedMemory (QObject * parent = nullptr)
virtual ~QSharedMemory ()
bool attach (QSharedMemory::AccessMode mode = ReadWrite)
const void * constData () const
bool create (qsizetype size , QSharedMemory::AccessMode mode = ReadWrite)
void * data ()
const void * data () const
bool detach ()
QSharedMemory::SharedMemoryError error () const
QString errorString () const
bool isAttached () const
QString key () const
bool lock ()
QString nativeKey () const
void setKey (const QString & key )
void setNativeKey (const QString & key )
qsizetype size () const
bool unlock ()

详细描述

QSharedMemory 提供对由多线程、多进程共享内存段的访问。它还为单线程 (或单进程) 提供锁定内存的独占访问手段。

当使用此类时,要意识到以下平台差异:

  • Windows:QSharedMemory 不拥有共享内存段。当所有线程或进程拥有实例化的 QSharedMemory (被附加到特定共享内存段) 销毁其 QSharedMemory 实例或退出时,Windows 内核会自动释放共享内存段。
  • 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 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.
  • 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.

    The POSIX backend can be explicitly selected using the -feature-ipc_posix option to the Qt configure script. If it is enabled, the QT_POSIX_IPC macro will be defined.

  • 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:

    • The key must be in the form <application group identifier>/<custom identifier> ,如文档化 here and here .
    • The key length is limited to 30 characters.
    • 当进程退出时,不会清理命名的共享内存条目,因此,重启应用程序并以相同名称重新创建共享内存会失败。要解决此问题,回退到附加现有共享内存条目:
      QSharedMemory shm("DEVTEAMID.app-group/shared");
      if (!shm.create(42) && shm.error() == QSharedMemory::AlreadyExists)
          shm.attach();
      									
  • Android: QSharedMemory is not supported.

记住锁定共享内存采用 lock () 在读写共享内存之前,和记住释放锁采用 unlock () 在完成后。

QSharedMemory 自动销毁共享存储段,当最后实例化的 QSharedMemory 从段分离时,且不保留段引用。

警告: QSharedMemory 以特定 Qt 方式改变键,除非另外指定。与非 Qt 应用程序的互操作的达成是通过首先创建默认共享内存采用 QSharedMemory(),然后设置本机键采用 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 ()) 且应使用用户定义机制达成这种保护。

Alternative: Memory-Mapped File

Another way to share memory between processes is by opening the same file using 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 QTemporaryFile 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 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, 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 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.

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 this very purpose. Do note that it is world-readable and writable (like /tmp and /var/tmp ), so one must be careful of the contents revealed there. Another alternative is to use the XDG Runtime Directory (see QStandardPaths::writableLocation () 和 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 committing the file's contents to permanent storage. This request is performed by passing the FILE_ATTRIBUTE_TEMPORARY flag in the dwFlagsAndAttributes 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.

成员类型文档编制

enum QSharedMemory:: AccessMode

常量 描述
QSharedMemory::ReadOnly 0 共享内存段是只读的。不允许写入共享内存段。尝试写入采用 ReadOnly 创建的共享内存段,会导致程序中止。
QSharedMemory::ReadWrite 1 允许读写共享内存段。

enum QSharedMemory:: SharedMemoryError

常量 描述
QSharedMemory::NoError 0 没有出现错误。
QSharedMemory::PermissionDenied 1 操作失败,因为调用者没有所需权限。
QSharedMemory::InvalidSize 2 创建操作失败,因为请求大小无效。
QSharedMemory::KeyError 3 操作失败,因为键无效。
QSharedMemory::AlreadyExists 4 A create () 操作失败,因为具有指定键的共享内存段已存在。
QSharedMemory::NotFound 5 An attach () 失败,因为找不到具有指定键的共享内存段。
QSharedMemory::LockError 6 试图 lock () 共享内存段失败,因为 create () 或 attach () 失败和返回 false,或因为发生系统错误在 QSystemSemaphore::acquire ().
QSharedMemory::OutOfResources 7 A create () 操作失败,因为没有足够内存可用于填充请求。
QSharedMemory::UnknownError 8 发生其它事情且很糟糕。

成员函数文档编制

QSharedMemory:: QSharedMemory (const QString & key , QObject * parent = nullptr)

构造共享内存对象采用给定 parent 并将其键设为 key 。因为有设置键,其 create () 和 attach () 函数可以被调用。

另请参阅 setKey (), create (),和 attach ().

QSharedMemory:: QSharedMemory ( QObject * parent = nullptr)

此函数重载 QSharedMemory()。

构造共享内存对象采用给定 parent 。由于构造函数未设置共享内存对象的键,因此,共享内存对象没有附加底层共享内存段。必须设置键采用 setKey () 或 setNativeKey () 先于 create () 或 attach () 可以使用。

另请参阅 setKey ().

[virtual] QSharedMemory:: ~QSharedMemory ()

析构函数清零键,强制共享内存对象 detach 从其底层共享内存段。若此共享内存对象是连接到共享内存段的最后一个, detach () 操作会销毁共享内存段。

另请参阅 detach () 和 isAttached ().

bool QSharedMemory:: attach ( QSharedMemory::AccessMode mode = ReadWrite)

试图将进程附加到共享内存段,由传递给构造函数的键标识或调用 setKey () 或 setNativeKey ()。访问 mode is ReadWrite 默认情况下。它还可以为 ReadOnly 。返回 true 若附加操作成功。若返回 false,调用 error () 以确定发生何种错误。在附加共享内存段后,可以获得共享内存的指针通过调用 data ().

另请参阅 isAttached (), detach (),和 create ().

const void *QSharedMemory:: constData () const

返回共享内存段内容的 const 指针,若有附加一个的话。否则,返回 null。记得锁定共享内存采用 lock () 在读写共享内存之前,和记住释放锁采用 unlock () 在完成后。

另请参阅 attach () 和 create ().

bool QSharedMemory:: create ( qsizetype size , QSharedMemory::AccessMode mode = ReadWrite)

创建共享内存段 size 字节采用传递给构造函数的键,设置采用 setKey () 或设置采用 setNativeKey (),然后附加到新共享内存段采用给定访问 mode 并返回 true 。若由键标识的共享内存段已存在,不履行附加操作和 false 被返回。当返回值为 false ,调用 error () 以确定发生何种错误。

另请参阅 error ().

void *QSharedMemory:: data ()

返回共享内存段内容的 const 指针,若有附加一个的话。否则,返回 null。记得锁定共享内存采用 lock () 在读写共享内存之前,和记住释放锁采用 unlock () 在完成后。

另请参阅 attach ().

const void *QSharedMemory:: data () const

此函数重载 data()。

bool QSharedMemory:: detach ()

从共享内存段分离进程。若这是附加到共享存储段的最后进程,则共享存储段由系统释放 (即:销毁内容)。函数返回 true 若它分离了共享内存段。若它返回 false ,通常意味着段未被附加或被另一进程锁定。

另请参阅 attach () 和 isAttached ().

QSharedMemory::SharedMemoryError QSharedMemory:: error () const

返回指示是否发生错误的值,且若如此,指示错误是什么。

另请参阅 errorString ().

QString QSharedMemory:: errorString () const

返回最后发生错误的文本描述。若 error () 返回 错误值 ,调用此函数以获取描述错误的文本字符串。

另请参阅 error ().

bool QSharedMemory:: isAttached () const

返回 true 若此进程被附加到共享内存段。

另请参阅 attach () 和 detach ().

QString QSharedMemory:: key () const

返回的键赋值采用 setKey () 到此共享内存,或 null 键若尚未赋值键,或段使用的是 nativeKey ()。键是 Qt 应用程序用来标识共享内存段的标识符。

可以找到由操作系统使用的本机、特定平台键,通过调用 nativeKey ().

另请参阅 setKey () 和 setNativeKey ().

bool QSharedMemory:: lock ()

这是锁供此进程访问的共享内存段的信号量,并返回 true 。若另一进程已锁定段,此函数阻塞直到锁被释放为止。然后,获取锁并返回 true 。若此函数返回 false ,意味着已忽略的 false 返回来自 create () 或 attach (),已设置键采用 setNativeKey () 或 QSystemSemaphore::acquire () 失败由于未知系统错误。

另请参阅 unlock (), data (),和 QSystemSemaphore::acquire ().

QString QSharedMemory:: nativeKey () const

返回用于此共享内存对象的本机、特定平台键。本机键是操作系统用于标识共享内存段的标识符。

可以使用本机键访问 Qt 尚未创建的共享内存段,或将共享内存访问授予非 Qt 应用程序。

另请参阅 setKey () 和 setNativeKey ().

void QSharedMemory:: setKey (const QString & key )

设置平台无关 key 对于此共享内存对象。若 key 与当前键相同,函数将什么都不做而返回。

可以调用 key () 以检索平台无关键。在内部, QSharedMemory 将此键转换成平台特定键。若改为调用 nativeKey (),将获得特定平台的转换键。

若共享内存对象被附加到底层共享内存段,它将 detach 从它在设置新键之前。此函数不履行 attach ().

另请参阅 key (), nativeKey (),和 isAttached ().

void QSharedMemory:: setNativeKey (const QString & key )

设置本机,特定平台, key 对于此共享内存对象。若 key 与当前本机键相同,函数将什么都不做而返回。若只想为段赋值键,应调用 setKey () 代替。

可以调用 nativeKey () 以检索本机键。若有赋值本机键,调用 key () 将返回 null 字符串。

若共享内存对象被附加到底层共享内存段,它将 detach 从它在设置新键之前。此函数不履行 attach ().

应用程序将不可移植,若设置本机键。

另请参阅 nativeKey (), key (),和 isAttached ().

qsizetype QSharedMemory:: size () const

返回共享内存段的附加大小。若未附加共享内存段,返回 0。

注意: 段大小可能大于请求大小,被传递给 create ().

另请参阅 create () 和 attach ().

bool QSharedMemory:: unlock ()

释放共享内存段锁并返回 true ,若锁目前由此进程保持。若段未被锁定,或锁由另一进程所保持,则什么也不会发生并返回 false。

另请参阅 lock ().