Interprocess

1. 基础概念

1. 进程间共享信息方式

① 两个进程使用文件共享信息。为了获得数据,每个进程使用通常的文件读/取机制。当更新/读取一个多进程共享的文件时,我们需要一些同步机制来保护读取和写入。

② 两个进程共享驻留在操作系统内核的信息。例如,传统的消息队列。同步机制由操作系统的内核来维护。

③ 两个进程共享一个内存区域。这就是通常的共享内存或内存映射文件。一旦某进程建立了内存区域,则进程们可以像使用其他内存片段一样读/写数据,而不需要调用操作系统内核。这种方式也需要人工进行进程间的同步处理。

2. 进程进程间机制持久化

① 进程持久化:机制一直保留,直到所有打开机制的进程关闭、退出或崩溃。

② 内核持久化:机制一直存在,直到操作系统内核重启或是机制被显式删除。

③ 文件系统持久化:机制一直存在直到被显式删除。

机制

持久化

共享内存

内核或文件系统

内存映射文件

文件系统

进程共享互斥类型

进程

进程共享信号量

进程

进程共享条件变量

进程

文件锁

进程

消息队列

内核或文件系统

具名互斥量

内核或文件系统

具名信号量

内核或文件系统

具名条件变量

内核或文件系统

2. Boost.Interprocess基础

 1. 共享内存(shared_memory_object

1. 什么是共享内存?

共享内存是最快速的进程间通信机制。操作系统在几个进程的地址空间上映射一段内存,然后这几个进程可以在不需要调用操作系统函数的情况下在那段内存上进行读/写操作。但是,在进程读写共享内存时,我们需要一些同步机制。

2. 使用共享内存的基本步骤

① 向操作系统申请一块能在进程间共享的内存shared_memory_object。使用者能够使用共享内存对象创建/销毁/打开这个内存:一个代表内存的对象,这段内存能同时被映射至多个进程的地址空间。

② 将这个内存的部分或全部与被调用进程的地址空间联系起来mapped_region。操作系统在被调用进程的地址空间上寻找一块足够大的内存地址范围,然后将这个地址范围标记为特殊范围。在地址范围上的变化将会被另一个映射了同样的共享内存对象的进程自动监测到。

3. shared_memory_object

① 头文件:#include   

② 命名空间:boost::interprocess

③ 成员函数:
     1. 
shared_memory_object(): 构造函数。多个重载版本的构造函数,用于构造一个共享内存映射。非空参数版本失败将会抛出异常。构造的共享内存映射的初始大小为0,需要使用truncate为其设置大小。
     2. 
truncate(): 设置共享内存映射的大小。
     3. 
swap(): 交换函数。
     4. 
get_name(): 返回共享内存的名称。
     5. 
get_size():检测指定大小的内存在共享内存中是否可用。
     6. 
get_mode(): 返回共享内存的操作模式。 
     7. get_mapping_handle(): 返回共享内存映射的句柄。不会抛出异常。
     8. 
remove(): 静态成员函数。用于删除共享内存映射。

 2. 内存映射文件file_mapping

1. 什么是内存映射文件

文件映射是一个文件的内容和一个进程的部分地址空间的关联。系统创建一个文件映射来联系文件和进程的地址空间。一个映射区域是地址空间的一部分,进程使用这部分来访问文件的内容。一个单个的文件映射可以有几个映射区域,以便使用者能关联文件的多个部分和进程的地址空间,而不要映射整个文件至地址空间,因为文件的大小可能会比整个进程地址空间还大(在通常32位系统下的一个9GBDVD镜像文件)。进程使用指针从文件读写数据,就好像使用动态内存一样。

2. 使用内存映射文件的基本步骤

① 创建一个可映射的对象用来代表文件系统中已经创建的某个文件file_mapping。这个对象将用于创建此文件的多个映射区域。

② 将整个或部分文件与被调用进程的地址空间关联mapped_region。操作系统在被调用进程的地址空间上搜寻一块足够大的内存地址范围,并且标记地址范围为一个特殊范围。在地址范围上的任何改变会自动被另一个映射了同一个文件的进程检测到,并且这些改变会自动传输至磁盘上。

3. file_mapping

① 头文件:#include 

② 命名空间:boost::interprocess

③ 成员函数:
    1. file_mapping: 构造函数。多个重载版本的构造函数,用于构造一个内存映射文件。非空参数版本失败将会抛出异常
    2. swap(): 交换函数。
    3. get_name(): 返回内存映射文件的名称。
    4. get_mode(): 返回内存映射文件的操作模式。
    5. get_mapping_handle(): 返回共内存映射文件的句柄。不会抛出异常。
    6. remove(): 静态成员函数。用于删除内存映射文件。

 3. mapped_region

将共享内存或内存映射文件映射到进程的地址空间。共享内存或内存映射文件只有在关联到进程的地址空间后,才能获取到地址,然后进行操作。

① 头文件:#include   

② 命名空间:boost::interprocess

③ 成员函数:
    1. 
mapped_region(): 关联共享内存/内存映射文件的部分或全部到进程地址空间。
    2. 
swap(): 交换函数。
    3. get_size(): 返回映射的大小。其值在构造函数中关联时指定。
    4. get_address(): 返回关联到的的进程地址。
    5. get_mode(): 返回操作模式。
    6. flush(): 针对内存映射文件,将缓存写入到硬盘,异步方式将在写入操作完成之前返回,同步方式则是等待完全写入后才返回。
    7. shrink_by(): 收缩当前映射区域。
    8. advise(): 通知函数。
    9. get_page_size(): 静态成员函数。返回系统页面大小。

映射区域并不能承载的对象:原始指针,引用,虚函数。

映射区域的带静态变的类,静态成员在每个进程有一个副本,改变某一个进程的静态变量不会影响其他进程中的值。

 4. offset_ptr


 5. 同步机制

具名同步机制:虽然每个进程都使用一个不同的对象来访问资源,但这些进程均使用相同的底层资源。具名实用工具在处理简单的同步任务时要简单些,因为进程不需要创建共享内存区域以及构建同步机制。

匿名同步机制:所有进程共享同一对象当使用内存映射对象获得同步工具的自动持久化属性时,匿名实用工具可以被序列化至磁盘。你可以在内存映射文件上构建一个同步工具,重启系统,再次映射此文件,从而再次使用此同步化工具。这在具名实用工具的方式下是不行的。

1. 互斥变量(mutex

所有互斥变量都提供lock(),try_lock(),timed_lock(),unlock()方法。上述方法都能抛出interprocess_exception错误。

#include 提供了scoped_lock,进程间的局部锁。

① interprocess_mutex一个非递归的、匿名的互斥量,它能够被置于共享内存和内存映射文件中。#include 

② interprocess_recursive_mutex:一个递归的、匿名的互斥量,它能够被置于共享内存和内存映射文件中。#include 

③ named_mutex:一个非递归的、具名的互斥量。#include 

④ named_recursive_mutex:一个递归的、具名的互斥量。#include 

2. 条件变量(condition

3. 信号量(semaphore

4. 升级互斥类型

5. 文件锁

 6. 消息队列message_queue

消息队列类似于一个消息的链表。线程可以放置消息至此队列,也能从队列中删除消息。每个消息可以有一个优先级以便高优先级消息在低优先级消息前被读取。每个消息都有一些属性:优先级消息长度数据(如果长度大于0

消息队列仅在进程间拷贝原始字节,并且不发送对象。这意味着如果我们想通过消息队列发送一个对象,对象必须首先被二进制序列化

message_queue

1. 头文件:#include

2. 命名空间:boost::interprocess

3. 成员函数:

① message_queue(): 构造函数。传入操作模式,队列名称,消息最大数量,消息体最大大小,许可等级,以构造或打开一个消息队列。发生错误时将抛出一个interprocess_error错误。

② send(): 发送消息到消息队列。若消息队列满,将一直阻塞。发生错误时将抛出一个interprocess_error错误。

③ try_send():  发送消息到消息队列。若消息队列满,立即返回失败。发生错误时将抛出一个interprocess_error错误。

④ timed_send():发送消息到消息队列。若消息队列满,将继续尝试发送,直至超时。发生错误时将抛出一个interprocess_error错误。

⑤ receive(): 从消息队列中获取一个消息。若消息队列为空,将一直阻塞。发生错误时将抛出一个interprocess_error错误。

⑥ try_receive():从消息队列中获取一个消息。若消息队列为空,立即返回失败。发生错误时将抛出一个interprocess_error错误。

⑦ timed_receive():从消息队列中获取一个消息。若消息队列为空,将继续尝试获取,直至超时。发生错误时将抛出一个interprocess_error错误。

⑧ get_max_msg(): 返回最大消息数。 从不抛出异常。

⑨ get_max_msg_size(): 返回消息体最大大小。 从不抛出异常。

⑩ get_num_msg(): 返回队列中当前消息数量。从不抛出异常。

11 remove(): 静态成员函数。 删除一个消息队列。若消息队列正在被其他进程使用将返回false。从不抛出异常。

3. Boost.Interprocess进阶

Boost.Interprocess提供4个托管内存片段类:

① 用于管理共享内存映射区域(basic_managed_shared_memory 类)。

② 用于管理内存映射文件(basic_managed_mapped_file类)。

③ 用于管理在堆上分配(运算符new)的内存缓冲区(basic_managed_heap_memory类)。

④ 用于管理一个用户提供的固定大小缓冲区(basic_managed_external_buffer类)。

托管内存段的最重要的服务是:

① 动态分配内存段的部分。

② 在内存段中构建C++对象。这些对象可以是匿名的或我们可以为其关联一个名称。

③ 具名对象的搜索能力。

④ 许多特性的定制:内存分配算法,索引类型或字符类型。

⑤ 原子构造和析构,以便如果内存段在两个进程间共享,我们能够创建两个关联同一名称的对象,从而简化同步。

 1. 托管的共享内存(managed_shared_memory

 2. 托管的内存映射文件(managed_mapped_file




windows

  1.windows下原生中共享内存持久化类型为进程级别,为了实现boost中对于共享内存统一的持久化级别:内核或文件系统,windows下使用了内存映射文件来模拟共享内存。文件默认路径为:C:\ProgramData\boost_interprocess\。
  2.在windows下的实际项目使用时发现一个问题:某一进程创建了消息队列后,另一进程去打开该消息队列,结果发现打开操作结果表现为该消息队列在C:\ProgramData\boost_interprocess\下对应的映射文件被删除了,后有同事复现,可能原因是windows未激活导致,激活后程序工作正常。



部分内容摘抄自 http://blog.csdn.net/great3779/article/details/7222195
@boost笔记系列总结自罗剑锋的《Boost库完全开发指南》第三版、Boost 1.61.0 Library Documentation、boost 1.51 源码。

10-01 03:13