Boost.Interprocess 使用 Windows COM 库来实现某些功能,并使用并发模型 COINIT_APARTMENTTHREADED
初始化它。如果调用线程已经为另一种并发模型初始化了 COM 库,Boost.Interprocess 会优雅地处理这种情况,并为已初始化的模型使用 COM 调用。如果由于某种原因,您希望 Boost.Interprocess 使用另一种模型初始化 COM 库,请在包含 Boost.Interprocess 之前定义宏 BOOST_INTERPROCESS_WINDOWS_COINIT_MODEL
为以下值之一
COINIT_APARTMENTTHREADED_BIPC
COINIT_MULTITHREADED_BIPC
COINIT_DISABLE_OLE1DDE_BIPC
COINIT_SPEED_OVER_MEMORY_BIPC
共享内存 (shared_memory_object
) 在 Windows 中使用内存映射文件实现,放置在共享文档文件夹中的共享目录中 (SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Common AppData
)。此目录名称是通过会话管理器的注册表值获取的最后启动时间。自 Boost 1.74 以来,此行为是默认行为。
旧版本的 Boost.Interprocess (直到 Boost 1.48 ) 使用 COM 调用 (通过宏 BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME
),但该时间戳在虚拟机和时间调整中不可靠。直到 (Boost 1.74) 的后续版本使用 EventLog 启动事件作为时间戳,但一些用户发现如果系统运行时间过长,此事件可能不存在于事件日志中。您可以定义 BOOST_INTERPROCESS_BOOTSTAMP_IS_EVENTLOG_BASED 来强制使用基于事件日志的时间戳。
在任何错误情况下(未定义共享文档文件夹或无法获取启动时间),库都会抛出错误。您仍然可以通过定义自己的目录作为共享目录来使用 Boost.Interprocess。当您的共享目录是编译时常量时,在使用库时定义 BOOST_INTERPROCESS_SHARED_DIR_PATH
(以及 Windows 系统中的 BOOST_INTERPROCESS_SHARED_DIR_WPATH),该路径将用于放置共享内存文件。当您必须在运行时确定共享目录时,定义 BOOST_INTERPROCESS_SHARED_DIR_FUNC
并实现以下函数
namespace boost { namespace interprocess { namespace ipcdetail { void get_shared_dir(std::string &shared_dir); //wstring overload is only needed for Windows systems void get_shared_dir(std::wstring &shared_dir); } } }
如果定义了 BOOST_USE_WINDOWS_H
,则会包含 <windows.h> 和其他 Windows SDK 文件,否则库会声明所需的功能和结构,以减少包含这些重型标头的影响。
在没有 POSIX 共享内存支持的系统上,共享内存对象实现为内存映射文件,使用放置在 "/tmp" 中的目录,该目录可以包含(如果定义了 BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME
)最后启动时间(如果操作系统支持)。与 Windows 一样,在任何错误情况下,如果无法获取此目录,库都会抛出错误。当您的共享目录是编译时常量时,在使用库时定义 BOOST_INTERPROCESS_SHARED_DIR_PATH
,该路径将用于放置共享内存文件。当您必须在运行时确定共享目录时,定义 BOOST_INTERPROCESS_SHARED_DIR_FUNC
并实现该函数
namespace boost { namespace interprocess { namespace ipcdetail { void get_shared_dir(std::string &shared_dir); } } }
提交的地址空间是内核可能必须提供的虚拟内存(交换空间或物理内存/RAM)总量,如果所有应用程序都决定访问它们从内核请求的所有内存。默认情况下,Linux 允许进程提交比系统中可用虚拟内存更多的虚拟内存。如果未访问该内存,则实际上不使用物理内存 + 交换空间。
这种行为的原因是 Linux 试图优化 fork 进程的内存使用;fork() 创建进程空间的完整副本,但在过度提交的内存中,在这个新的 fork 实例中,只有已写入的页面才需要由内核实际分配。如果应用程序访问的内存超过可用内存,则内核必须以硬方式释放内存:OOM(内存不足)杀手会选择一些进程杀死以回收内存。
Boost.Interprocess 无法更改此行为,用户在访问共享内存时可能会遭受 OOM 杀手的困扰。根据 内核文档,Linux 内核支持多种过度提交模式。如果您的应用程序需要非杀死保证,则应更改此过度提交行为。
从 FreeBSD 11 开始,声明了宏 _POSIX_THREAD_PROCESS_SHARED。但是,默认行为与 Linux 上的行为不同。如果要使用此功能,根据 libthr(3) 的手册页,您应该检查 sysctl 的 kern.ipc.umtx_vnode_persistent
如果您希望映射文件在最后一个句柄关闭后仍然有用,请将此变量设置为 1。
Boost.Interprocess 在 MacOS 中使用 POSIX 共享内存来实现 MacOS 中的共享内存类。
但是,沙盒应用程序无法使用 System V (XSI) 信号量和 POSIX 信号量,并且默认情况下无法使用共享内存。但是,通过指定请求加入应用程序组的权利,应用程序可以使用这些技术与其他应用程序组成员进行通信。
请参阅 Apple 文档 了解有关沙盒应用程序中进程间通信的限制和指南。
许多人贡献了想法和修订,因此这里是感谢他们的地方
<boost/interprocess/containers/*.hpp>
标头。它们是 2011 年 Boost.Container 的原始来源,但不再维护。作为长期过渡,Boost.Interprocess 一直维护这些标头以实现兼容性。它们将在未来的 Boost 发行版中删除。segment_manager
现在使用新的实现,该实现支持过度对齐类型并修复了一些现有错误。您可以通过在包含 Boost.Interprocess 标头之前将 BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI
定义为 1
来获得 Boost 1.87 之前的 ABI。interprocess::ipcdetail::intermodule_singleton
在 MinGW 上不起作用").
浮点数 不精确 导致
get_current_process_creation_time 导致 `intermodule_singleton` 失败"
).
offset_ptr
添加了 natvis 定义。非常感谢 Braden Ganetsky。unordered_map_index
类。TimedLockable
要求,条件变量实现 wait_until/wait_for
操作。boost::date_time::ptime
, std::time_point
或 boost::chrono::time_point
。wchar_t
API 支持。以下类已更新为支持 wchar_t
名称file_mapping
managed_mapped_file
managed_shared_memory
managed_windows_shared_memory
shared_memory_object
windows_shared_memory_object
file_lock
named_condition
named_condition_any
named_mutex
named_recursive_mutex
named_semaphore
named_sharable_mutex
named_upgradable_mutex
message_queue
BOOST_INTERPROCESS_FORCE_NATIVE_EMULATION
宏选项,以禁用对进程共享同步原语使用通用仿真代码,而改用本机 POSIX 或 Winapi 函数。BOOST_INTERPROCESS_BOOTSTAMP_IS_SESSION_MANAGER_BASED
现在是默认值。您可以通过定义 BOOST_INTERPROCESS_BOOTSTAMP_IS_EVENTLOG_BASED
来获得 Boost 1.73 之前的行为。旧的且已损坏的 Boost 1.54 之前的行为 (BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME
) 不再可用。BOOST_INTERPROCESS_BOOTSTAMP_IS_SESSION_MANAGER_BASED
。此选项从与会话管理器关联的注册表值派生用于命名共享内存放置文件夹的唯一启动时间戳。此选项仅适用于 Vista 及更高版本的系统,并且可能比默认版本更稳定。offset_ptr
性能并消除了任何未定义的行为。不同编译器不再需要特殊情况。unique_ptr
,现在将 boost::interprocess::unique_ptr 转发到来自 Boost.Move 的通用 boost::movelib::unique_ptr
类。此实现更接近标准 std::unique_ptr
实现,并且维护得更好。BOOST_INTERPROCESS_MSG_QUEUE_CIRCULAR_INDEX
选项完全损坏,因此为了实现正常工作,必须进行 ABI 破坏性变更。BOOST_INTERPROCESS_SHARED_DIR_PATH
选项,用于定义共享目录,当共享内存对象实现为内存映射文件时,将放置在该目录中。BOOST_USE_WINDOWS_H
的支持。当定义此宏时,Interprocess 不会声明使用的 Windows API 函数和类型,而是包含所有需要的 Windows SDK 头文件,并使用 Windows SDK 声明的类型和函数。::boost:interprocess:windows_shared_memory
添加了 get_size
。LastBootupTime
在时间同步和休眠方面不稳定,实际上无法使用。如果您确实需要获取 Boost 1.54 之前的行为,请从命令行或 detail/workaround.hpp
中定义 BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME
。mapped_region
中添加了 shrink_by
和 advise
函数。message_queue
(旧的行为使用有序数组,导致过多的复制)。这应该会大大提高性能,但会破坏 ABI。旧的行为/ABI 可以通过在 boost/interprocess/detail/workaround.hpp
中取消定义宏 BOOST_INTERPROCESS_MSG_QUEUE_CIRCULAR_INDEX
来使用message_queue
插入时间,避免了常见情况下的优先级搜索(数组和循环缓冲区配置)。interproces_sharable_mutex
和 interproces_condition_any
。offset_ptr
性能。mapped_region::flush
提供了同步和异步刷新。mapped_region
中移除了 get_offset
方法,因为它没有实际用途,并且 m_offset
成员也没有其他用途。managed_shared_memory
中移除了 flush
,因为它根据 POSIX 是未指定的:"msync() 对共享内存对象或类型化内存对象的影响是未指定的" 。mapped_region::flush
启动磁盘刷新,但不保证在返回时完成,因为它不具有可移植性。semaphore
和 named_semaphore
。BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION
宏选项,以强制对进程共享同步原语使用通用模拟代码,而不是本机 POSIX 函数。boost::posix_time::pos_inf
值现在可以为定时函数进行可移植处理。iterator
更新为 const_iterator
,以跟上下一个标准草案。flat_map::erase()
函数中错误的 void
返回类型。mapped_region
添加了用于创建它的模式。shared_ptr
是可移动的并支持别名。shared_ptr
、weak_ptr
和 unique_ptr
的定义和构造。在文档中添加了对这些智能指针的解释和示例。pointer
定义为智能指针的分配器时,尽可能地使用原始指针。这提高了性能并缩短了编译时间。named_mutex
和 named_semaphore
。named_condition
已相应更改,以支持与 named_mutex
的互操作性。mapped_region
中的错误。aligned_allocate
和 allocate_many
函数。get_instance_name
、get_instance_length
和 get_instance_type
函数。rbtree_best_fit
中次优的缓冲区扩展错误。vector
中的泄漏。segment_manager
,以避免与模板实例化相关的代码膨胀。deque::clear()
和 deque::erase()
中的错误,它们被声明为私有。deque::erase()
中的错误。感谢 Steve LoBasso。atomic_dec32()
中的错误。感谢 Glenn Schrader。offset_ptr
指针激活。managed_mapped_file
创建的文件的压缩率很有用。boost::has_trivial_destructor
。此优化避免了调用具有平凡析构函数的元素的析构函数。has_trivial_destructor_after_move
特征。此优化避免了在元素被移动(许多可移动类型的情况)后,调用具有平凡析构函数的元素的析构函数。此技巧由 Howard Hinnant 提供。deallocate_free_chunks()
) 以手动从节点分配器中释放完全空闲的块。multiset
和 multimap
类中实现了 N1780 提案给 LWG 问题 233:关联容器中的插入提示。shared_memory_object.hpp
头文件而不是 shared memory.hpp
头文件的共享内存对象。mode_t
类型。mapped_region
构造函数不再需要从 memory_mappable 派生的类,但类必须满足 MemoryMappable 概念。一些关于 C++ 编程语言、C++ 内部原理、共享内存、分配器和容器的有用参考资料,用于设计 Boost.Interprocess。
我希望实现一些 Interprocess 功能,并且一些 Boost.Interprocess 代码可以做得更好。让我们看一些想法
Win32 版本的共享互斥锁和共享条件基于“自旋和等待”原子指令。这导致性能低下,并且不处理任何诸如优先级反转之类的问题。我们需要线程专家在这方面提供非常认真的帮助。我不确定这是否可以在用户级软件中实现。基于 Posix 的实现使用 PTHREAD_PROCESS_SHARED 属性将互斥锁放置在共享内存中,因此不存在此类问题。我不知道有任何实现可以为 Win32 模拟 PTHREAD_PROCESS_SHARED 属性。我们应该能够在内存映射文件中构建这些原语,以便我们可以像 POSIX 原语一样获得文件系统持久性。
当前 Interprocess 仅允许为基本命名对象使用基于 char 的名称。但是,一些操作系统对资源(例如,映射文件)使用 wchar_t 名称。将来,Interprocess 应该尝试提供可移植的窄/宽字符接口。为此,拥有 boost wstring <-> string 转换实用程序来以可移植的方式转换资源名称(转义可能与操作系统名称冲突的必要字符)将很有用。使用 boost::filesystem 路径来避免操作系统特定问题也很有趣。
Boost.Interprocess 没有为共享内存和同步对象定义安全属性。标准 C++ 也忽略文件的安全属性,因此添加安全属性将需要一些认真的工作。
Boost.Interprocess 提供了一个基于 Boost.Interprocess 原语(如互斥锁和条件变量)的进程共享消息队列。我希望开发更多机制,例如面向流的命名 fifo,以便我们可以将其与 iostream 接口包装器一起使用(我们可以模仿 Unix 管道)。
C++ 需要更复杂的机制,如果能在 C++ 中拥有一个面向流和数据报的类似 PF_UNIX 的机制就太好了。对于非常快速的进程间远程调用,Solaris doors 是一个有趣的替代方案,可以在 C++ 中实现。但是实现类似 PF_UNIX 的套接字和 doors 的工作量将是巨大的(并且在用户级库中可能很困难)。有网络专家志愿者吗?