Chinaunix首页 | 论坛 | 博客
  • 博客访问: 580964
  • 博文数量: 104
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1559
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-21 00:58
个人简介

锻炼精神,首先要锻炼肉体

文章分类

全部博文(104)

文章存档

2018年(1)

2016年(1)

2015年(101)

2014年(1)

我的朋友

分类: 信息化

2015-06-04 17:40:31

Anonymous shared memory for UNIX systems
Unix 系统中的匿名共享内存

Creating a shared memory segment and mapping it can be a bit tedious when several process are involved.
当有多个进程参与到内存共享的时,创建一个共享内存段并将其依次映射到每个进程中需要执行许多冗长的方法

When processes are related via fork() operating system call in UNIX systems a simpler method is available using anonymous shared memory.
在 Unix 操作系统中,当进程中涉及到 fork 系统调用方法时可以通过使用匿名共享内存来简化方法执行步骤

This feature has been implemented in UNIX sysmtes mapping the device \dev\zero or just using the MAP_ANONYMOUS in a POSIX conformant
mmap system call.
Unix  操作系统中已经实现了这种(使用匿名共享内存)技术的,把 \dev\zero 作为映射实体来使用,或是使用 POSIX 中的 mmap 系统调用方法时
传入 MAP_ANONYMOUS 参数便可以。

This feature is wrapped in Boost.Interprocess using the anonymous_shared_memory() function, which returns a mapped_region object holding
an anonymous shared memory segment that can be shared by related processes.
这一功能已经被封装于 Boost.Interprocess 中,通过调用tade anonymous_shared_memory() 方法便可以使用,这个方法(Boost.Interprocess.anonmous_shared_memory())
将会返回存有一个匿名共享内存段且可以被在多个进程中共享使用的mapped_region 对象实例

Here is an example:
下面是代码实例:

点击(此处)折叠或打开

  1. #include <boost/interprocess/anonymous_shared_memory.hpp>
  2. #include <boost/interprocess/mapped_region.hpp>
  3. #include <iostream>
  4. #include <cstring>

  5. int main ()
  6. {
  7.    using namespace boost::interprocess;
  8.    try{
  9.       //Create an anonymous shared memory segment with size 1000
  10.       // 通过调用 boost::interprocess 中的 anonymous_shared_memory 方法并传入所需要字节数目
  11.      // 便可以创建一个 anonymous_shared_memory , 将其传入值 mapped_region 的构造方法中
  12.      // 便可以获取一个大小为 1000 字节的映射域
  13.       mapped_region region(anonymous_shared_memory(1000));

  14.       //Write all the memory to 1
  15.       // 接下来只要将操作方法作用于映射域即可使用匿名共享内存空间
  16.       std::memset(region.get_address(), 1, region.get_size());

  17.       //The segment is unmapped when "region" goes out of scope
  18.       // 一旦代码执行到这里 "}" 作用域外面,当前的进程便会解除与该匿名共享内存之间的映射关系
  19.       // 由该进程通过 fork() 生成的进程便可以获取到该匿名共享内存
  20.    }
  21.    catch(interprocess_exception &ex){
  22.       std::cout << ex.what() << std::endl;
  23.       return 1;
  24.    }
  25.    return 0;
  26. }
Once the segment is created , a fork() call can be used so that region is used to communicate two related processes.
该共享内存段一经创建结束,通过调用 fork() 函数,便可将刚刚创建好的映射域用于两个相关进程间的通信中。



Native windows shared memory
Windows 系列 平台中的原生共享内存

Windows operating system also offers shared memory, but the lifetime of this shared memory is very different to kernel or filesystem lifetime.
Windows 系列的操作系统中也实现了共享内存,但是该共享内存的生存周期与操作系统内核与文件系统的生存周期之间有着很大的差异.

The shared memory is created backed by the pagefile and it's automatically destoryed when the last process attached to the shared memory is destoryed.
(Windows中的) 共享内存主要是通过操作系统中的页面文件(也就是虚拟内存) 来实现的,也就是说当最后一个访问该共享内存的进程生命周期结束的时候,
该共享内存将会被系统自动的销毁.
<也就是说, boost::interprocess 在 windows 系列平台上进行共享内存仿真的基础是该共享内存的声明周期与内核/文件系统的生命周期一样的,
但是 Windows 中所谓的共享内存是通过虚拟内存来实现的,它的生命周期是进程级别的, 进程生命周期 << 内核/文件系统生命周期 ,且虚拟内存空间
类似于局部变量,使用之后会被系统自动回收,内核/文件系统,类似于使用 malloc 分配空间的变量,需要使用者手工调用指定方法进行回收>

Because of this reason , there is no effective way to simulate kernel or filesystem persistence using the native windows shared memory and Boost.Interprocess
emulates shared memory using memory mapped files.
正是因为这个原因(Windows 中原生共享内存生命周期与内核/文件系统生命周期不一致), 无法使用 Windows 原生共享内存技术来实现内核或是文件系统中可持久化的存储的模拟,所以 Boost.Interprocess通过使用内存映射文件来仿真共享内存。

This assures portability between POSIX and Windows operating systems.
这种解决方法确保了 boost::interprocess 代码在支持 POSIX 的操作系统 与 Windows 操作系统之间可移植性.

However,accessing native windows shared memory is a common request of Boost.Interprocess users because they want to access to shared memory
created with other process that don't use Boost.Interprocess.
然而,使用者普遍也是需要通过 Boost.Interprocess 来获取 windows 上原生共享内存对象的,因为他们(在编程的时候)会有 访问 windows 系统而不是 Boost::Interprocess 所创建的共享内存这样的需求.


In order to manage the native windows shared memory Boost.Interprocess offers the windows_shared_memory class .
为了可以操纵 Windows 原生的共享内存,Boost.Interprocess 函数库中提供了 windows_shared_memory 类(并在其中封装了相应的方法)

Windows shared memory creation is a bit different from portable shared memory creation : the size of the segment must be specified when creating the object
and can't be specified through truncate like with the shared memory object.
Windows 原生共享内存的创建方法与可移植性共享内存的创建有一点不同: 在创建共享内存对象的时候,需要指定内存段的大小,并且在成功创建共享内存对象
之后是不可以通过类似 truncate 的方法来重新指定内存空间大小的.

Taken in care that when the last process attached to a shared memory is destoryed the shared memory is destoryed so there is no persistency with native
windows shared memroy.
使用 windows_shared_memory 的时候值得注意的便是,当最后一个访问共享内存的进程生命周期完结的时候,它所访问的共享内存空间将会被销毁,
所以在 windows 原生共享内存中根本没有"持久化存储" 这样一种概念.

Sharing memory between services and user applications is also different.
用于服务进程与用户应用程序进程间的共享内存多少是有所不同的.

To shared memory between services and user applications the name of the shared memory must start with the global namespace prefix “Global\\”
在服务进程与用户应用进程之间所共享内存的命名必须要以全局命名空间 "Global\\" 这个的字段作为前缀

This global namespace enables processes on multiple client sessions to communicate with a service application.
这个全局命名空间名称可以让多个处于会话期的客户端进程与同一个服务进程进行通信

The server component can create the shared memory in the global namespace .
以全局命名空间来创建共享内存空间是由服务进程中的服务器组件完成的.

Then a client session can use the "Global" prefix to open that memory
随后,来自客户端的会话进程便可以将 "Global" 作为前缀打开该共享内存空间

The creation of a shared memory object in the global namespace from a session other than session zero is a privileged operation.
在创建共享内存对象的多个请求中,如果有来自全局命名空间的请求,那么它的申请优先于其余申请被执行

Let's repeat the same example presented for the portable shared memory object : A server process creates a shared memory object,maps it and
initializes all the bytes to a value.
让我们来用可移植性的共享内存对象为例,重新描述一下上述的问题 : 服务进程创建了一个共享内存对象,成功的将其映射到映射域中,
并且将其内部全部字节初始化为某个数值.

After that , a client process opens the shared memory , maps it , and checks that the data is correctly initialized.
在指向上述操作之后,一个客户进程打开该共享内存对象,将其映射到自己的地址进程空间中,并检查该共享内存空间中的数据是否是被正确的初始化了.

Take in care that if the server exists before the client connects to the shared memory the client connections will fail ,
because the shared memory segment is destoryed when no process is attached to the memory.
注意这一点: 如果在该客户端尝试连接到该共享内存之前,该服务进程仍旧是运行的话,<含义便是: 服务进程运行于客户进程对共享内存连接之前,
更直白的说就是: 客户进程对共享进程进行连接、但还没连接上,server 进程已经断开连接> 那么这次来自 client 的连接将会以失败收场,失败的原因便是:
在client 连接尝试共享内存但还没有连接上 且 server 进程断开了与共享内存的连接的时候,该共享内存端处于一个没有任何一个进程与之相连的状态,
处于这种状态的内存段(不属于任何一个进程)会被系统回收并销毁。
(类似于 A 把盘子递给 B, B 还没有接住 A 就放手,盘子掉到地上摔碎,类似于没有指针指向的使用 malloc 的变量空间)

This is the server process :
请看下面服务器进程的代码:

点击(此处)折叠或打开

  1. #include <boost/interprocess/windows_shared_memory.hpp>
  2. #include <boost/interprocess/mapped_region.hpp>
  3. #include <cstring>
  4. #include <cstdlib>
  5. #include <string>

  6. int main(int argc, char *argv[])
  7. {
  8.    using namespace boost::interprocess;

  9.    if(argc == 1){ //Parent process
  10.       //Create a native windows shared memory object.
  11.       // 首先我们使用我们讨论过的 windows_shared_memory 来创建一个使用 windows 原生共享内存(基于虚拟内存或是页面)
  12.       windows_shared_memory shm (create_only, "MySharedMemory", read_write, 1000);

  13.       //Map the whole shared memory in this process
  14.       // 将刚刚在当前进程中所创建的共享内存映射到 映射域中
  15.       mapped_region region(shm, read_write);

  16.       //Write all the memory to 1
  17.      // 通过映射域将共享空间中的所有字节置为数值 1
  18.       std::memset(region.get_address(), 1, region.get_size());

  19.       //Launch child process
  20.       // 在这里我们将重新组装一下调用命令
  21.       // 将 ./test 修改为 ./test child 这样便让 argv[0] = ./test argv[1] = child  且 argc != 1
  22.      //  从而再次执行当前程序的时候,会走下面 else 的这个分支
  23.      
  24.     // 其中,通过 std::system 方法来替代用于手动在控制台中输入命令
  25.       std::string s(argv[0]); s += " child ";
  26.       if(0 != std::system(s.c_str()))
  27.          return 1;

  28.       //windows_shared_memory is destroyed when the last attached process dies...
  29.      // 注意: 这个地方便是我们在文中讨论过的转折点: 这个时候共享内存空间变成了 爹不疼(服务进程断开了与该空间的映射)
  30.      // 且 娘不爱 (而 client 进程虽然运行,但是还没有在 server 断开映射之前 创建与该共享空间的映射关系) 的"孤儿" ,
  31.      // 结果就是该共享空间被系统(孤儿院, WC, 我太有才了...) 所回收
  32.    
  33.     // 这个地方也是因为使用 Windows 原生共享系统的生命周期=进程生命周期的一种操作的缺陷,也是为何 Boost.Interprocess 为何通篇
  34.    // 强调为何使用 map files 来替代 windows 中基于虚拟空间来实现共享内存
  35.    }
  36. //-----------------下面的代码是 client 进程要执行的分支代码 ----------------------------------------------
  37.    else{
  38.       //Open already created shared memory object.
  39.       // 首先打开已经在 server 中创建好的 共享内存空间对象 (其实早就被系统回收了)
  40.       windows_shared_memory shm (open_only, "MySharedMemory", read_only);

  41.       //Map the whole shared memory in this process
  42.       // 然后将在当前进程中打开的共享内存空间对象映射到 映射域中(mapped_region )
  43.       mapped_region region(shm, read_only);

  44.       //Check that memory was initialized to 1
  45.       // 检查一下映射域中的所有数值是否全部被初始化为数值 1
  46.       char *mem = static_cast<char*>(region.get_address());
  47.       for(std::size_t i = 0; i < region.get_size(); ++i)
  48.          if(*mem++ != 1)
  49.             return 1; //Error checking memory
  50.       return 0;
  51.    }
  52.    return 0;
  53. }

As we can see , native windows shared memory needs synchronization to make sure that the shared memory won't be destoryed before the client is launched.
从上面的这个例子我们可以得知,windows系统 中的原生共享内存在使用的时候需要制定一些同步机制来确保:在 client 进程成功映射到共享内存空间之前,该共享内存空间不会被系统所销毁.



XSI shared memory
XSI 共享内存

In many UNIX systems , the OS offers another shared memory memory mechanism, XSI (X/Open System Interfaces) shared memory segments,also known
as "System V" shared memory.
在众多 UNIX 操作系统中会提供另一种内存共享的存储机制,XSI (开放式系统接口)内存共享段,同样以 " V 型系统"所著称.

This shared memory mechanism is quite popular and portable , and its's not based in file-mapping sematics, but it uses special functions
(shmget , shmat, shmdt, shmctl ....)
这套共享内存机制同样十分的主流,并且有着优良的可移植性,该共享内存机制并不基于文件-映射语义规则,但其中却有着很多特殊的方法
(shmget, shmat, shmdt, shmctl ....)

Unlike POSIX shared memory segments , XSI shared memory segments are not identified by names but by "keys" usually created with ftok.
与 POSXI(可移植操作系统接口)实现共享内存段的手法不同的是,XSI 靠方法 ftok 生成的 '键' 来标识每个共享内存段,而不是通过为每个共享内存段起名字的方式.

XSI shared memory segments have kernel lifetiem and must be explicitly removed.
通过 XSI 机制创建的共享内存段具有和内核相同的生命周期,所以它必须被手动显示的移除才可以.
<难道这么说来与使用 malloc 声明创建的变量都与 filesystem/kernel 有着相同的相同的生命周期?>

XSI shared memory does not support copy-on-write and partial shared memory mapping but it supports anonymous shared memory.
XSI 共享内存并不支持共享内存空间的写时拷贝和部分空间的映射这些技术,但它支持创建匿名的共享内存.

Boost.Interprocess offers simple() and managed ( ) shared memory classes to ease the use of 
XSI shared memory.
Boost.Interprocess 库中提供者 XSI 共享内存机制的简单实现( 中封装了相关的方法) 和用来管理 XSI 共享内存对象的类 ( ) ,
目的是为了让 XSI 共享内存对象使用起来更加的方便.


It also wraps key creation with the simple  class.
在 Boost.Interprocess 库中同样封装了用于生成 标识不同共享内存空间的 '键' 的类 ( class ).

Let's repeat the same example presented for the portable shared memory object : A server process creates a shared memory object, maps it and initializes all
the bytes to value.
让我们使用 XSI 手法,重新实现一下如何创建并使用一个可移植的共享内存空间对象 : 在服务进程中创建了一个共享内存对象,并将该共享内存映射到
映射域中,然后通过映射域将共享内存中的每个字节初始化为某个数值.

After that , a client process opens the shared memory, maps it , and checks that the data is correctly initialized.
执行上述操作之后,创建一个客户进程,在该客户进程中打开共享空间,并将该共享内存映射到该客户进程中的 映射域中,并通过该映射域(mapped-region)
来检查该共享空间中的每个字节是否被 server 进程正确的初始化了

This is the server process :
下面的便是 server 进程的代码:

点击(此处)折叠或打开

  1. #include <boost/interprocess/xsi_shared_memory.hpp>
  2. #include <boost/interprocess/mapped_region.hpp>
  3. #include <cstring>
  4. #include <cstdlib>
  5. #include <string>

  6. using namespace boost::interprocess;

  7. // 在这里定义了 remove_old_shared_memory 方法,
  8. // 该方法通过删除传入到方法中的 key 参数来找到该 key 所一一映射的共享内存空间实体
  9. // 并将该共享内存实体从系统中移除

  10. void remove_old_shared_memory(const xsi_key &key)
  11. {
  12.    try{
  13.       xsi_shared_memory xsi(open_only, key);
  14.       xsi_shared_memory::remove(xsi.get_shmid());
  15.    }
  16.    catch(interprocess_exception &e){
  17.       if(e.get_error_code() != not_found_error)
  18.          throw;
  19.    }
  20. }

  21. int main(int argc, char *argv[])
  22. {
  23.    if(argc == 1){ //Parent process
  24.     // 通过判断argc 是否等于 1 来判断当前执行代码的进程是 父进程还是通过父进程调用 std::system(....) 来launch 的子进程
  25.     // 显而易见,if 分支所对应的代码是 父进程执行的代码

  26.       //Build XSI key (ftok based)
  27.      // 首先通过前文提到过的 boost::interprocess 函数库中的 xsi_key 类实例来为将要创建的共享内存对象生成唯一的 '键'
  28.       xsi_key key(argv[0], 1);
  29. //
  30. //    为了防止,该 key 原本就有与之对应的共享内存-值,所以在使用该 '键' 创建共享内存对象之前,先来执行一下删除该 '键' 对应的 '值' 的方法调用 
  31.      remove_old_shared_memory(key);

  32.       //Create a shared memory object.
  33.       // 通过该键值创建一个 共享内存实体 , 并传参指定 该共享内存为 1000 字节大小
  34.       xsi_shared_memory shm (create_only, key, 1000);

  35.       //Remove shared memory on destruction
  36.       // 创建结构体 shm_remove ,该结构体的析构函数将会在 main 函数结束之后自动的被调用
  37.       // 将移除共享空间的方法写入到该结构体的析构函数中 会得到这样的结果:
  38.       // 在 main 函数结束的时,调用结构体析构函数的同时也会执行该共享空间的移除方法
  39.  
  40.       struct shm_remove
  41.       {
  42.          int shmid_;
  43.          shm_remove(int shmid) : shmid_(shmid){}
  44.          ~shm_remove(){ xsi_shared_memory::remove(shmid_); }
  45.       } remover(shm.get_shmid());

  46.       //Map the whole shared memory in this process
  47.       // 将刚刚在当前进程中所 申请创建的共享内存空间 全部映射到映射域中(mapped_region 也是一个类)
  48.       mapped_region region(shm, read_write);

  49.       //Write all the memory to 1
  50.      // 通过调用 memset 方法通过 mapped_region 类实例 region 把共享内存空间中的全部 1000 个字节
  51.     // 置为数值 1
  52.       std::memset(region.get_address(), 1, region.get_size());

  53.       //Launch child process
  54.       // 下面开始运行子进程,细节参考上一段代码中的注释

  55.       std::string s(argv[0]); s += " child ";
  56.       if(0 != std::system(s.c_str()))
  57.          return 1;
  58.    }
  59.    else{
  60.       //Build XSI key (ftok based)
  61.       // 首先这个 else 分支对应的是 子进程将要执行的代码,
  62.       // 调用 子进程与 父进程唯一的命令语句 不同便是 父进程: ./demo    | 子进程 ./demo child
  63.      // 生成 key 所基于的字符串都是该代码生成的二进制可执行文件的名称 "./demo"
  64.     // 所以,子进程和父进程 通过调用 xsi_key 生成的 key 是完全相同的

  65.       xsi_key key(argv[0], 1);

  66.       //Create a shared memory object.
  67.       // 现在子进程获得了与父进程用于创建共享内存对象 同样的 'key'
  68.       // 使用该 'key' 来打开父进程创建的共享内存对象
  69.       //------------------------> 刚刚不是说过了,XSI 的共享内存生存周期与内核相同,不是 Windows 中
  70.       //------------------------> 生存周期== 进程生存周期的 原生共享内存,所以,父进程即便释放了刚刚创建的内存空间,该共享内存也不会被系统回收                                                                   
  71.       xsi_shared_memory shm (open_only, key);

  72.       //Map the whole shared memory in this process
  73.      // 将刚刚获取的共享内存映射到 映射域(mapped_region )中
  74.       mapped_region region(shm, read_only);

  75.       //Check that memory was initialized to 1
  76.       // 通过 mapped_region 类实例 来访问共享空间中的每一个 byte
  77.       // 检查它们是否与 父进程中初始化的数值是一样的
  78.       char *mem = static_cast<char*>(region.get_address());
  79.       for(std::size_t i = 0; i < region.get_size(); ++i)
  80.          if(*mem++ != 1)
  81.             return 1; //Error checking memory
  82.    }
  83.    return 0;
  84. }


to be continued
阅读(2176) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~