共享内存

Qt 提供了两种技术与其他系统进程共享内存: QSharedMemory 和使用 QFile 的内存映射文件。与其他进程共享的内存通常称为“段”,尽管在具有分段的内存模型的处理器上过去可能实现了特定的段,但在任何现代操作系统中都不是这样。共享内存段仅仅是操作系统确保所有参与进程都可以访问的内存区域。

注意:段在内存中的地址几乎对于参与共享的每个进程都是不同的。因此,应用程序必须小心地只共享位置无关的数据,例如原生的 C++ 类型或此类类型的数组。

使用 QSharedMemory 锁定内存

QSharedMemory 提供了一个简单的 API,用于创建指定大小或附加到由其他进程创建的共享内存段。此外,它提供了对内部 QSystemSemaphore 的锁定和解锁整个段的方法。

通过“键”在整个系统中全局识别共享内存段和系统信号量,在 Qt 中由 QNativeIpcKey 类表示。此外,根据操作系统,Qt 可能支持多个不同的内存共享后端;有关更多信息和技术限制,请参阅原生 IPC 键 文档。

QSharedMemory 设计为仅在同一特权级别(也就是说,不与不受信任的其他进程,例如其他用户启动的进程)内共享内存。对于支持的后端,QSharedMemory 将创建只允许具有相同特权级别的进程附加到的段。

通过内存映射文件共享内存

大多数文件可以使用 QFile::map() 映射到内存,如果未指定 MapPrivateOption 选项,则对映射段的所有写入操作都将被所有已映射相同文件的其他进程观察到。不能映射到内存的文件除外,例如网络共享中的远程文件或位于某些文件系统中的文件。即使操作系统允许将远程文件映射到内存,文件上的 I/O 操作可能也会被缓存和延迟,从而使得真正的内存共享成为不可能。

此解决方案具有独立于任何后端API并获得简单与非Qt应用交互的显著优势。由于QTemporaryFileQFile,应用可以使用这个类来实现清理语义以及在共享内存段中创建唯一段。

为了实现对共享内存段的锁定,应用需要部署自己的机制。一种方法可能是使用QLockFile。另一种更低成本的方法是在预定的偏移量中使用QBasicAtomicInteger或std::atomic。在某些操作系统上,可能还有更高级别的锁定原语;例如,在Linux中,可以将“pshared”标识符设置在传递给pthread_mutex_create()的互斥锁属性中,以指示互斥锁位于共享内存段中。

请注意,操作系统可能会尝试将共享内存中的任何写入提交到持久存储。这可能是所希望的,也可能是因为文件本身是临时的而造成的性能损失。在这种情况下,应用应定位一个基于RAM的文件系统,例如Linux中的tmpfs(参见QStorageInfo::fileSystemType()),或者将标志传递给原生文件打开函数来通知操作系统避免将内容提交到存储。

可以使用基于文件的共享内存与不受信任的进程通信。在这种情况下,应用应十分小心。文件可能会被截断/缩小,从而导致访问文件大小之外的内存的应用崩溃。

Linux关于内存映射文件的提示

在现代Linux系统中,虽然文件通常是一个tmpfs挂载点,但这不是必需的。但是,/dev/shm目录必须是一个tmpfs,它存在就是为了共享内存。请注意,它是可被世界读取和写入的(如/tmp/var/tmp),因此应用必须小心那里揭示的内容。另一个选择是使用XDG运行时目录(参见QStandardPaths::writableLocation()和QStandardPaths::RuntimeLocation),在Linux系统使用systemd时,这是一个特定用户的tmpfs

一个更安全的解决方案是使用memfd_create(2)创建“memfd”并使用进程间通信传递文件描述符,如QDBusUnixFileDescriptor或允许QProcess的子进程继承它。“memfds”也可以被密封以防止缩小,因此在与具有不同权限级别进程通信时是安全的。

FreeBSD关于内存映射文件的提示

FreeBSD也有memfd_create(2),并可以使用与Linux相同的技巧将文件描述符传递给其他进程。它没有默认安装的临时文件系统。

Windows关于内存映射文件的提示

在Windows上,应用可以请求操作系统避免将文件内容保存到持久存储。此请求通过传递到CreateFile Win32函数的dwFlagsAndAttributes参数中的FILE_ATTRIBUTE_TEMPORARY标志,或者传递到_open()低级函数的_O_SHORT_LIVED标志,或者通过将“T”修改器包括到fopen() C运行时函数中来实现。

还有标志,用于通知操作系统在文件的最後柄被关闭时删除该文件(FILE_FLAG_DELETE_ON_CLOSE_O_TEMPORARY以及"D"修饰符),但请注意,所有试图打开文件的进程都必须同意使用或不使用此标志。不匹配将可能导致资源共享违规和文件无法打开。

© 2024 Qt公司有限公司。本文档中的文档贡献均为各自所有者的版权。提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其相关标志是芬兰及/或其他国家Qt公司的商标。所有其他商标均为其各自所有者的财产。