Boost C++ 库

……世界上最受推崇和设计精良的 C++ 库项目之一。 Herb SutterAndrei AlexandrescuC++ 编码标准

PrevUpHomeNext

鸣谢、说明和链接

Windows 用户说明
Linux 用户说明
FreeBSD 用户说明
macOS 用户说明
感谢……
发行说明
书籍和有趣的链接
未来改进……

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 启动事件作为时间戳,但一些用户发现,如果系统运行时间较长,则 Event Log 中可能缺少此事件。您可以通过定义 BOOST_INTERPROCESS_BOOTSTAMP_IS_EVENTLOG_BASED 来强制使用基于 Event Log 的时间戳。

在任何错误情况下(未定义共享文档文件夹或无法获得启动时间),库都会抛出错误。您仍然可以通过定义您自己的目录作为共享目录来使用 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

  • kern.ipc.umtx_vnode_persistent:默认情况下,由内存中映射文件支持的共享锁会在对应文件的页面的最后一次取消映射时自动销毁,这在 POSIX 中是允许的。将 sysctl 设置为 1 会使此类共享锁对象持续存在,直到 vnode 被虚拟文件系统回收。请注意,如果文件未打开且未映射,内核可能会随时回收它,这使得此 sysctl 的作用不如听起来那么大。

如果您希望映射文件在最后一个句柄关闭后仍然可用,请将此变量设置为 1。

Boost.Interprocess 在 macOS 中使用 POSIX 共享内存来实现共享内存类。

但是,沙盒应用程序无法使用 System V (XSI) 信号量和 POSIX 信号量,并且默认情况下无法使用共享内存。但是,通过指定请求应用程序组成员资格的权利,应用程序可以使用这些技术与该应用程序组的其他成员进行通信。

请参阅 Apple 文档 了解有关如何在沙盒应用程序上使用进程间通信的限制和指南

许多人贡献了想法和修订,所以这是感谢他们的地方。

  • 感谢所有对该库感兴趣并下载和测试快照的人。
  • 感谢 Francis AndreAnders Hybertz 的想法和建议。其中许多尚未实现,但我希望在库获得一些稳定性后将它们包含在内。
  • 感谢 Matt DoyleSteve LoBassoGlenn SchraderHiang Swee ChiangPhil EndecottRene RiveraHarold PirtlePaul RyanShumin WuMichal WozniakPeter JohnsonAlex OttShane GuillorySteven WoodingKim Barrett 的错误修复和库测试工作。
  • 感谢 Martin Adrian 建议使用进程间框架来处理用户自定义缓冲区。
  • 感谢 Synge Todo 提供的 boostbook-doxygen 补丁,用于改进进程间文档。
  • 感谢 Olaf Krzikalla 提供的 Intrusive 库。我借鉴了该库中的一些想法来改进红黑树的实现。
  • 感谢 Daniel James 提供的 unordered_map/set 家族以及他在分配器方面提供的帮助。他出色的无序实现一直是设计异常安全容器的参考。
  • 感谢 Howard Hinnant 提供的巨大帮助,特别是解释分配器交换、移动语义以及开发可升级互斥锁和锁转移功能。
  • 感谢 Pavel Vozenilek 持续的代码审查、建议、代码贡献和帮助。他是进程间库的主要支持者。该库在他的许多宝贵建议下不断发展壮大。
  • 最后,感谢所有 Boost 社区贡献者。 C++ 万岁!
  • 删除已损坏的 unordered_map_index 类。
  • 修复由于多个 Boost 库迁移到 C++11 而导致的 C++03 支持损坏问题。
  • 为 mapped_region 添加了对特定于平台的标志的支持(工单 #8030)
  • 修复的错误
  • ABI 破坏性变更:将 Windows 中的 bootstamp 函数更改为使用 EventLog 服务启动时间作为系统启动时间。以前使用的来自 WMI 的 LastBootupTime 在时间同步和休眠状态下不稳定,实际上不可用。如果您确实需要获得 Boost 1.54 之前的行为,请从命令行或 detail/workaround.hpp 定义 BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME
  • 修复了 GCC -Wshadow 警告。
  • 实验性的多重分配接口得到了改进,并再次更改。仍然不稳定。
  • 将已弃用的 BOOST_NO_XXXX 宏替换为较新的 BOOST_NO_CXX11_XXX 宏。
  • ABI 破坏性变更:为了提高效率,更改了节点池分配器的内部结构。
  • 修复了错误 #7795
  • mapped_region 中添加了 shrink_byadvise 函数。
  • ABI 破坏性变更: 使用循环缓冲区索引重新实现了 message_queue(旧的行为使用有序数组,导致过度复制)。这应该会大大提高性能,但会破坏 ABI。可以通过在 boost/interprocess/detail/workaround.hpp 中取消定义宏 BOOST_INTERPROCESS_MSG_QUEUE_CIRCULAR_INDEX 来使用旧的行为/ABI。
  • 改进了 message_queue 的插入时间,避免了常见情况下的优先级搜索(数组和循环缓冲区配置)。
  • 实现了 interproces_sharable_mutexinterproces_condition_any
  • 改进了 offset_ptr 的性能。
  • 添加了整数溢出检查。
  • mapped_region::flush 添加了同步和异步刷新。
  • 源码和 ABI 破坏性变更:从 mapped_region 中删除了 get_offset 方法,因为它没有实际用途,并且 m_offset 成员也不用于其他任何用途。
  • 源码和 ABI 破坏性变更:从 managed_shared_memory 中删除了 flush,因为它根据 POSIX 未指定:“msync() 对共享内存对象或类型化内存对象的影响未指定”
  • 修复了错误 #7152
  • 修复了错误 #3750#6727#6648
  • Windows 中的共享内存再次具有内核持久性:内核 bootstamp 和 WMI 已经过一些修复和优化。这会导致与 Boost 1.48 和 1.49 不兼容,但用户可以在 Windows 配置部分注释 #define BOOST_INTERPROCESS_HAS_KERNEL_BOOTTIME 以获得 Boost 1.48 和 Boost 1.49 的行为。
  • 修复了错误 #2796#4031#4251#4452#4895#5077#5120#5123#5230#5197#5287#5294#5306#5308#5392#5409
  • 添加了对自定义 offset_ptr 的支持,并允许创建可以在 32 位和 64 位进程之间共享的自定义托管段。
  • Windows 中的共享内存再次具有文件系统生命周期:用于获取可靠时间戳的内核 bootstamp 和 WMI 导致了很多麻烦。
  • 支持 Mac OS 中的 POSIX 共享内存。
  • ABI 破坏性变更: 泛型 semaphorenamed_semaphore 现在使用原子操作实现了更高效的实现。
  • 在具有活动杀毒软件的 Windows 平台上实现了更强大的文件打开功能。
  • Windows 共享内存现在创建在“共享文档”文件夹中,以便可以在服务和进程之间共享。
  • 修复了 bug #2967#2973#2992#3138#3166#3205
  • 添加了实验性的 stable_vector 容器。
  • shared_memory_object::remove 现在具有 POSIX unlink 语义,并且添加了 file_mapping::remove 以获得与映射文件相关的 POSIX unlink 语义。
  • Windows 中的共享内存现在具有内核生命周期,而不是文件系统生命周期:系统重启时共享内存将消失。
  • 更新了移动语义。
  • 修复了 bug #2722#2729#2766#1390#2589
  • 更新了文档,显示右值引用函数而不是模拟函数。
  • 现在更多不可复制的类可以移动。
  • 移动构造函数和赋值运算符现在将移动的对象置于默认构造状态,而不仅仅是交换内容。
  • 修复了多个 bug(#2391#2431#1390#2570#2528)。
  • 容器现在可以在递归类型中使用。
  • 添加了 BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION 宏选项,强制使用泛型模拟代码来实现进程共享同步原语,而不是本地 POSIX 函数。
  • 向容器添加了就地插入成员。
  • boost::posix_time::pos_inf 值现在对于定时函数的可移植性得到处理。
  • 为了跟上下一标准草案,更新了一些容器函数的参数,从 iterator 改为 const_iterator
  • 文档修复。
  • 为 UNIX 系统添加了匿名共享内存。
  • 修复了 void flat_map::erase() 函数错误的返回类型。
  • 修复了托管内存类中缺少的移动语义。
  • 为共享内存和映射文件托管类添加了 copy_on_write 和 open_read_only 选项。
  • ABI 破坏性变更: 向 mapped_region 添加了用于创建它的模式。
  • 修正了 void 分配器中的实例化错误。
  • shared_ptr 可移动且支持别名。
  • 添加了辅助实用程序,以简化 shared_ptrweak_ptrunique_ptr 的定义和构造。在文档中添加了这些智能指针的解释和示例。
  • 优化了 vector
    • 1) 现在在使用将 pointer 定义为智能指针的分配器时,尽可能使用原始指针。这提高了性能并缩短了编译时间。
    • 2) 一些元编程技巧,在类型具有平凡的复制构造函数或赋值运算符时避免使用 move_iterator,从而提高性能。
    • 3) 将自定义算法替换为标准算法,以利用优化的标准算法。
    • 4) 删除了未使用的代码。
  • ABI 破坏性变更: 容器不再派生自分配器,以避免出现分配器可能定义与容器成员函数同名的虚函数的问题。这会将容器函数转换为虚函数,并且如果返回类型不导致协变返回,则可能会禁止其中一些函数。分配器现在作为内部结构的基类存储。
  • 在支持该选项的系统中,使用 POSIX 命名信号量实现了 named_mutexnamed_semaphorenamed_condition 已相应更改以支持与 named_mutex 的互操作性。
  • 减少了节点和自适应分配器的模板膨胀,将节点实现提取到仅依赖于内存算法的类中,而不是段管理器 + 节点大小 + 节点数……
  • 修复了 UNIX 中 mapped_region 的 bug,当提供映射地址但区域映射到另一个地址时。
  • 向托管内存段添加了 aligned_allocateallocate_many 函数。
  • 改进了有关托管内存段的文档。
  • Boost.Interprocess 容器现在记录在参考部分。
  • 更正了错别字和文档错误。
  • 向托管内存段添加了 get_instance_nameget_instance_lengthget_instance_type 函数。
  • 修正了 rbtree_best_fit 中次优缓冲区扩展的 bug。
  • 添加了段管理器中命名对象和唯一对象的迭代。
  • 修复了 vector 中的内存泄漏。
  • 添加了对 Solaris 的支持。
  • 优化了 segment_manager 以避免与模板实例化相关的代码膨胀。
  • 修复了 UNIX 的 bug:没有在共享内存名称的开头添加斜杠('/'),这导致某些 UNIX 系统出现错误。
  • 修复了 VC-8.0 的 bug:核心 offset_ptr 函数中的函数内联中断。
  • 代码示例已更改为使用新的 BoostBook 代码导入功能。
  • 向内存算法添加了对齐内存分配函数。
  • 修复了 deque::clear()deque::erase() 的 bug,它们被声明为私有的。
  • 修复了 deque::erase() 的 bug。感谢 Steve LoBasso。
  • 修复了 atomic_dec32() 的 bug。感谢 Glenn Schrader。
  • 改进了采用迭代器的 (multi)map/(multi)set 构造函数。如果迭代器范围已排序,则这些构造函数现在具有线性时间。
  • ABI 破坏性变更: (multi)map/(multi)set 现在减少了它们的节点大小。颜色位嵌入在父指针中。现在,在大多数系统中,节点的大小是 3 个指针的大小。此优化适用于原始指针和 offset_ptr 指针。
  • (multi)map/(multi)set 现在在赋值运算符中重用旧节点的内存。
  • ABI 破坏性变更: 实现了基于侵入式容器的节点容器。这节省了代码大小,因为许多实例共享相同的算法。
  • 更正了代码,使其可以使用 Visual C++ 8.0 编译。
  • 添加了在内存算法和段管理器中将空闲内存清零的功能。此功能出于安全原因以及为了提高使用 managed_mapped_file 创建的文件的压缩率而有用。
  • 在托管内存段中添加了对侵入式索引类型的支持。侵入式索引节省了额外的内存分配来分配索引,因为只需要一次分配,我们就可以为值、名称和挂钩分配空间以将对象插入索引中。
  • 创建了新的索引类型:iset_index。它是一个基于侵入式集合(rb-tree)的索引。
  • 创建了新的索引类型:iunordered_set_index。它是一个基于伪侵入式无序集合(哈希表)的索引。
  • ABI破坏:侵入式索引iset_index现在是默认索引类型。
  • 优化向量以利用boost::has_trivial_destructor。此优化避免调用具有平凡析构函数的元素的析构函数。
  • 优化向量以利用has_trivial_destructor_after_move特性。如果元素已被移动(许多可移动类型的情况),此优化将避免调用具有平凡析构函数的元素的析构函数。此技巧由Howard Hinnant提供。
  • 添加安全检查以避免分配器和命名构造函数中的整数溢出错误。
  • 向向前和向后扩展函数添加了对齐检查。
  • 修复了PPC原子函数中的错误。
  • 修复了创建和打开托管段时的竞争条件错误。
  • 添加了自适应池。
  • 源代码破坏:更改节点分配器的模板参数顺序以使其更易于使用。
  • 添加了对原生Windows共享内存的支持。
  • 添加了更多测试。
  • 更正了参考部分中私有函数的存在。
  • 添加了函数(deallocate_free_chunks())以手动从节点分配器中释放完全空闲的块。
  • 在进程间multisetmultimap类中实现了N1780提案以解决LWG问题233:关联容器中的插入提示
  • 源代码破坏:现在使用共享内存对象,包括shared_memory_object.hpp头文件,而不是shared memory.hpp
  • ABI破坏:初始化托管共享内存和内存映射文件时更改了全局互斥锁。此更改试图最大限度地减少死锁。
  • 源代码破坏:将共享内存、内存映射文件和映射区域的打开模式更改为单个mode_t类型。
  • 在包含DateTime头文件之前添加了额外的WIN32_LEAN_AND_MEAN,以避免在Windows中使用Interprocess和Asio时出现套接字重定义错误。
  • ABI破坏mapped_region构造函数不再需要从memory_mappable派生的类,但类必须满足MemoryMappable概念。
  • 向basic_string添加了就地重新分配功能。
  • ABI破坏:重新实现并优化了小型字符串优化。窄字符串类在32位系统中具有零字节开销,内部缓冲区为11字节!
  • 向容器添加了移动语义。提高了使用容器的容器时的性能。
  • ABI破坏:节点容器(list、slist、map/set)的末尾节点现在嵌入在容器中,而不是使用分配器分配。这允许无异常抛出的移动构造函数并提高性能。
  • ABI破坏slistlist容器现在具有常数时间size()函数。容器的大小作为成员添加。

一些关于C++编程语言、C++内部机制、共享内存、分配器和容器的有用参考,用于设计Boost.Interprocess

  • 关于多线程和POSIX的优秀书籍:"Programming with Posix Threads"David R. Butenhof
  • UNIX进程间通信的圣经:"UNIX Network Programming, Volume 2: Interprocess Communications"W. Richard Stevens
  • 当前STL分配器问题:"Effective STL"Scott Meyers
  • 我的C++圣经:"Thinking in C++, Volume 1 & 2"Bruce Eckel and Chuck Allison
  • 每个C++程序员都应该阅读的书:"Inside the C++ Object Model"Stanley B. Lippman
  • 必读书籍:"ISO/IEC TR 18015: Technical Report on C++ Performance"ISO WG21-SC22 members.

我有一些想要实现的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的套接字和门的这项工作将是巨大的(并且在用户级库中可能很困难)。有没有网络专家志愿者?


PrevUpHomeNext