Boost C++ 库

...世界上最受尊敬和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

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 启动事件作为时间戳,但一些用户发现如果系统运行时间过长,此事件可能不存在于事件日志中。您可以定义 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

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

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

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

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

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

许多人贡献了想法和修订,因此这里是感谢他们的地方

  • 感谢所有对该库表现出兴趣并下载和测试快照的人。
  • 感谢 Francis AndreAnders Hybertz 的想法和建议。其中许多尚未实现,但我希望在库获得一定稳定性后将其包含在内。
  • 感谢 Matt Doyle, Steve LoBasso, Glenn Schrader, Hiang Swee Chiang, Phil Endecott, Rene Rivera, Harold Pirtle, Paul Ryan, Shumin Wu, Michal Wozniak, Peter Johnson, Alex Ott, Shane Guillory, Steven WoodingKim Barrett 的错误修复和库测试。
  • 感谢 Martin Adrian 建议使用 Interprocess 框架用于用户定义的缓冲区。
  • 感谢 Synge Todo 的 boostbook-doxygen 补丁,以改进 Interprocess 文档。
  • 感谢 Olaf Krzikalla 的 Intrusive 库。我从他的库中汲取了一些改进红黑树实现的想法。
  • 感谢 Daniel James 的 unordered_map/set 系列以及他在分配器方面的帮助。他出色的 unordered 实现一直是设计异常安全容器的参考。
  • 感谢 Howard Hinnant 的惊人帮助,特别是解释分配器交换、移动语义以及开发可升级互斥锁和锁转移功能。
  • 感谢 Pavel Vozenilek 的持续审查过程、建议、代码和帮助。他是 Interprocess 库的主要支持者。该库在他的许多优秀建议下不断发展壮大。
  • 最后,感谢所有 Boosters。C++ 万岁!
  • 删除损坏的 unordered_map_index 类。
  • 修复由于多个 Boost 库迁移到 C++11 而导致的损坏的 C+03 支持。
  • 为 mapped_region 添加了平台特定标志的支持 (ticket #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。旧的行为/ABI 可以通过在 boost/interprocess/detail/workaround.hpp 中取消定义宏 BOOST_INTERPROCESS_MSG_QUEUE_CIRCULAR_INDEX 来使用
  • 改进了 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 中的共享内存再次具有内核持久性:内核启动时间戳和 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 中的共享内存再次具有文件系统生命周期:内核启动时间戳和 WMI 用于获取可靠时间戳导致了很多问题。
  • 在 Mac OS 中支持 POSIX 共享内存。
  • ABI 破坏性变更: 现在使用原子操作更有效地实现了通用 semaphorenamed_semaphore
  • 在具有活动防病毒软件的 Windows 平台上,文件打开更加健壮。
  • Windows 共享内存在“共享文档”文件夹中创建,以便可以在服务和进程之间共享
  • 修复了错误 #2967, #2973, #2992, #3138, #3166, #3205
  • 添加了实验性的 stable_vector 容器。
  • shared_memory_object::remove 现在具有 POSIX unlink 语义,并且添加了 file_mapping::remove 以使用映射文件获得 POSIX unlink 语义。
  • Windows 中的共享内存现在具有内核生命周期而不是文件系统生命周期:共享内存将在系统重启时消失。
  • 更新了移动语义。
  • 修复了错误 #2722, #2729, #2766, #1390, #2589,
  • 更新了文档,以显示右值引用函数而不是模拟函数。
  • 更多不可复制的类现在是可移动的。
  • 移动构造函数和赋值现在将移动后的对象置于默认构造状态,而不是仅仅交换内容。
  • 多个错误修复 ( #2391, #2431, #1390, #2570, #2528.
  • 容器现在可以在递归类型中使用。
  • 添加了 BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION 宏选项,以强制对进程共享同步原语使用通用模拟代码,而不是本机 POSIX 函数。
  • 为容器添加了就地插入成员
  • boost::posix_time::pos_inf 值现在可以为定时函数进行可移植处理。
  • 在容器中,将一些函数参数从 iterator 更新为 const_iterator,以跟上下一个标准草案。
  • 文档修复。
  • 为 UNIX 系统添加了匿名共享内存。
  • 修复了 flat_map::erase() 函数中错误的 void 返回类型。
  • 修复了托管内存类上缺失的移动语义。
  • 为共享内存和映射文件托管类添加了 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 中的错误。
  • 为托管内存段添加了 aligned_allocateallocate_many 函数。
  • 改进了关于托管内存段的文档。
  • Boost.Interprocess 容器现在已在参考部分中记录。
  • 更正了拼写错误和文档错误。
  • 为托管内存段添加了 get_instance_nameget_instance_lengthget_instance_type 函数。
  • 纠正了 rbtree_best_fit 中次优的缓冲区扩展错误。
  • 添加了在段管理器中迭代命名对象和唯一对象的功能。
  • 修复了 vector 中的泄漏。
  • 添加了对 Solaris 的支持。
  • 优化了 segment_manager,以避免与模板实例化相关的代码膨胀。
  • 修复了 UNIX 中的错误:共享内存名称的第一个字符没有添加斜杠 ('/'),导致某些 UNIX 系统中出现错误。
  • 修复了 VC-8.0 中的错误:核心 offset_ptr 函数中的函数内联损坏。
  • 代码示例已更改为使用新的 BoostBook 代码导入功能。
  • 为内存算法添加了对齐内存分配函数。
  • 修复了 deque::clear()deque::erase() 中的错误,它们被声明为私有。
  • 修复了 deque::erase() 中的错误。感谢 Steve LoBasso。
  • 修复了 atomic_dec32() 中的错误。感谢 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 现在是默认索引类型。
  • 优化了 vector 以利用 boost::has_trivial_destructor。此优化避免了调用具有平凡析构函数的元素的析构函数。
  • 优化了 vector 以利用 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 的伟大著作:"Posix 线程编程"David R. Butenhof
  • UNIX 进程间通信圣经:"UNIX 网络编程,卷 2:进程间通信"W. Richard Stevens
  • 当前 STL 分配器问题:"Effective STL"Scott Meyers
  • 我的 C++ 圣经:"C++ 编程思想,卷 1 和 2"Bruce Eckel 和 Chuck Allison
  • 每位 C++ 程序员都应该阅读的书籍:"深度探索 C++ 对象模型"Stanley B. Lippman
  • 必读:"ISO/IEC TR 18015: C++ 性能技术报告"ISO WG21-SC22 成员。

我希望实现一些 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 的工作量将是巨大的(并且在用户级库中可能很困难)。有网络专家志愿者吗?


PrevUpHomeNext