目录
一、前言
进程间通信描述了在同一台计算机上运行的程序之间交换数据的机制。它不包括网络通信。要在通过网络连接的不同计算机上运行的程序之间交换数据,请参阅第 32 章,其中介绍了 Boost.Asio。
二、何为内部进程
本章介绍了 Boost.Interprocess 库,它包含许多抽象操作系统特定接口以进行进程间通信的类。尽管不同操作系统之间进程间通信的概念相似,但接口可能会有很大差异。 Boost.Interprocess 提供独立于平台的访问。
虽然 Boost.Asio 可用于在同一台计算机上运行的进程之间交换数据,但 Boost.Interprocess 通常提供更好的性能。 Boost.Interprocess 调用针对运行在同一台计算机上的进程之间的数据交换而优化的操作系统函数,因此应该是在没有网络的情况下交换数据的首选。
三、共享内存Shared Memory
共享内存通常是最快的进程间通信形式。它提供了一个在进程之间共享的内存区域。一个进程可以将数据写入该区域,而另一个进程可以读取它。
在 Boost.Interprocess 中,类 boost::interprocess::shared_memory_object 用于表示共享内存。包含头文件 boost/interprocess/shared_memory_object.hpp 以使用此类。
示例 33.1。创建共享内存
#include <boost/interprocess/shared_memory_object.hpp>
#include <iostream>
using namespace boost::interprocess;
int main()
{
shared_memory_object shdmem{open_or_create, "Boost", read_write};
shdmem.truncate(1024);
std::cout << shdmem.get_name() << '\n';
offset_t size;
if (shdmem.get_size(size))
std::cout << size << '\n';
}
boost::interprocess::shared_memory_object 的构造函数需要三个参数。第一个参数指定是应该创建共享内存还是只打开共享内存。示例 33.1 处理了这两种情况。 boost::interprocess::open_or_create 如果共享内存已经存在则打开它,如果不存在则创建共享内存。
打开现有共享内存假定它之前已创建。为了唯一标识共享内存,分配了一个名称。该名称由传递给 boost::interprocess::shared_memory_object 的构造函数的第二个参数指定。
第三个参数决定进程如何访问共享内存。在示例 33.1 中,boost::interprocess::read_write 表示进程具有读写访问权限。
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
using namespace boost::interprocess;
int main()
{
shared_memory_object shdmem{open_or_create, "Boost", read_write};
shdmem.truncate(1024);
mapped_region region{shdmem, read_write};
std::cout << std::hex << region.get_address() << '\n';
std::cout << std::dec << region.get_size() << '\n';
mapped_region region2{shdmem, read_only};
std::cout << std::hex << region2.get_address() << '\n';
std::cout << std::dec << region2.get_size() << '\n';
}
创建 boost::interprocess::shared_memory_object 类型的对象后,操作系统中将存在相应的共享内存块。此内存区域的大小最初为 0。要使用该区域,请调用 truncate(),传入共享内存的大小(以字节为单位)。在示例 33.1 中,共享内存为 1,024 字节提供空间。只有在使用 boost::interprocess::read_write 打开共享内存时才能调用 truncate()。如果不是,则会抛出 boost::interprocess::interprocess_exception 类型的异常。可以重复调用 truncate() 来调整共享内存的大小。
创建共享内存后,可以使用 get_name() 和 get_size() 等成员函数查询共享内存的名称和大小。
因为共享内存用于不同进程之间交换数据,所以每个进程都需要将共享内存映射到自己的地址空间中。类 boost::interprocess::mapped_region 用于执行此操作。访问共享内存需要两个类(boost::interprocess::shared_memory_object 和 boost::interprocess::mapped_region)可能会让人感到意外。这样做是为了使类 boost::interprocess::mapped_region 也可用于将其他对象映射到进程的地址空间。
示例 33.2。将共享内存映射到进程的地址空间
要使用类 boost::interprocess::mapped_region,请包含头文件 boost/interprocess/mapped_region.hpp。 boost::interprocess::shared_memory_object 类型的对象必须作为第一个参数传递给 boost::interprocess::mapped_region 的构造函数。第二个参数决定了对内存区域的访问是只读还是读写。
示例 33.2 创建了两个 boost::interprocess::mapped_region 类型的对象。名为 Boost 的共享内存两次映射到进程的地址空间。使用成员函数 get_address() 和 get_size() 将映射内存区域的地址和大小写入标准输出。 get_size() 在这两种情况下都返回 1024,但 get_address() 的返回值对于每个对象是不同的。
注意
示例 33.2 以及随后的一些示例将导致 Visual C++ 2013 和 Boost 1.55.0 出现编译器错误。该错误在票 9332 中进行了描述。此错误已在 Boost 1.56.0 中修复。
示例 33.3。在共享内存中写入和读取数字
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
using namespace boost::interprocess;
int main()
{
shared_memory_object shdmem{open_or_create, "Boost", read_write};
shdmem.truncate(1024);
mapped_region region{shdmem, read_write};
int *i1 = static_cast<int*>(region.get_address());
*i1 = 99;
mapped_region region2{shdmem, read_only};
int *i2 = static_cast<int*>(region2.get_address());
std::cout << *i2 << '\n';
}
例 33.3 使用映射的内存区域来写入和读取一个数字。 region 将数字 99 写入共享内存的开头。 region2 然后读取共享内存中的相同位置并将数字写入标准输出流。即使 region 和 region2 代表进程中的不同内存区域,如前面示例中 get_address() 的返回值所示,程序也会打印 99,因为这两个内存区域都访问相同的底层共享内存。
示例 33.4。删除共享内存
#include <boost/interprocess/shared_memory_object.hpp>
#include <iostream>
using namespace boost::interprocess;
int main()
{
bool removed = shared_memory_object::remove("Boost");
std::cout << std::boolalpha << removed << '\n';
}
要删除共享内存,boost::interprocess::shared_memory_object 提供静态成员函数 remove(),它将要删除的共享内存的名称作为参数(参见示例 33.4)。
Boost.Interprocess 通过名为 boost::interprocess::remove_shared_memory_on_destroy 的类部分支持 RAII 习惯用法。它的构造函数需要现有共享内存的名称。如果该类的对象被销毁,则在析构函数中自动删除共享内存。
boost::interprocess::remove_shared_memory_on_destroy 的构造函数不会创建或打开共享内存。因此,这个类不是 RAII 习语的典型代表。
如果从不调用 remove(),即使程序终止,共享内存也会继续存在。共享内存是否自动删除取决于底层操作系统。 Windows 和许多 Unix 操作系统,包括 Linux,在系统重新启动后会自动删除共享内存。
Windows 提供了一种特殊的共享内存,一旦使用它的最后一个进程终止,它就会自动删除。访问在 boost/interprocess/windows_shared_memory.hpp 中定义的类 boost::interprocess::windows_shared_memory 以使用这种共享内存(参见示例 33.5)。
示例 33.5。使用特定于 Windows 的共享内存
#include <boost/interprocess/windows_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
using namespace boost::interprocess;
int main()
{
windows_shared_memory shdmem{open_or_create, "Boost", read_write, 1024};
mapped_region region{shdmem, read_write};
int *i1 = static_cast<int*>(region.get_address());
*i1 = 99;
mapped_region region2{shdmem, read_only};
int *i2 = static_cast<int*>(region2.get_address());
std::cout << *i2 << '\n';
}
boost::interprocess::windows_shared_memory 不提供成员函数 truncate()。相反,共享内存的大小需要作为第四个参数传递给构造函数。
四、总结
尽管类 boost::interprocess::windows_shared_memory 不可移植并且只能在 Windows 上使用,但当需要与使用这种特殊类型共享内存的现有 Windows 程序交换数据时,它还是很有用的。