What is shared memory ?
什么是共享内存?
Shared memory is the fastest interprocess communication mechanism .
共享内存是为了实现内存间最佳的通信方式而被设计出来的
The operating system maps a memory segment in the address space of several processes ,
操作系统将一段内存空间映射为内存地址并在在多个进程间进行共享
so that several processes can read and write in that memory segment without calling operating system functions .
这样的话(共享的这块内存)的多个进程便可以在不适用系统调用方法的前提下,通过向共享内存中读取和写入数据来交换信息、
However we need some kind of synchronization between processes that read and write shared memory .
然而,我们需要为多个进程通过读写共享内存进行通信 制定一些同步机制才可以。
Consider what happens when a server process wants to send an HTML file to a client process that resides in the same machine
using network mechanisms.
设想一下主机上的服务器进程要把一份 HTML 文件使用网络通信的方式发送给位于同一主机上的客户进程会发生什么 :
The server must read the file to the memory and pass it to the network functions , that copy that memory to the OS's internal
memory.
服务器进程一定是先把这份 HTML 文件从存储介质读入到(进程)内存中,然后将读入到(进程)内存中的数据发送到网络函数中,
网络函数会将(进程)内存中所存放的文件数据拷贝到操作系统中的内存中
The client uses the network functions to copy the data from the OS's internal memory to its own memory.
然后,和服务器进程运行在同一主机上的客户进程会使用网络通信函数将文件数据从操作系统内部内存拷贝到自己的(进程)内存中
As we can see , there are two copies , one from memory to the network and another one from the network to memory.
从上面的描述我们可以看出,一套流程走下来总共会涉及到两次拷贝操作,一次是从内存到网络缓冲空间的拷贝,
另一侧是从网络缓冲空间到(进程)内存空间的拷贝.
And those copies are made using operating system calls that normally are expensive .
上面使用过的拷贝方法是通过运行操作系统调用来实现的,无疑,这将花费很多的代价.
The server maps a shared memory in its address space and also gets access to a synchronization mechanism.
服务进程将位于它地址空间中映射一块开共享内存空间并且定制一套访问该共享内存的同步进制
The server obtains exclusive access to the memory using the synchronization mechanism and copies the file to memory.
服务器进程通过制定的同步机制,以独占的方式访问共享内存空间并将文件拷贝到该共享空间中
The client maps the shared memroy in its address space. Waits until the server releases the exclusive access and uses the
data .
客户进程将共享内存端映射到自己(进程)的地址空间中。当服务器进程独占式的访问结束之后,客户进程便会访问共享内存
中的数据了.
Creating memory segments that can be shared between processes
创建可以用于在多个进程间共享的内存段
To use shared memory, we have to perform 2 basic step :
(创建)使用共享内存,有 2 步基本操作
Request to the operating system a memory segment that can be shared between processes.
首先向操作系统申请一个可以用于多个进程之间共享的内存段
The user can create/destory/open this memroy using a shared memory object .
使用者(进程)可以通过一个 shared memory object 这个对象来将 创建,销毁,打开 操作施加于刚刚创建的内存块上面.
An object that represents memory that can be mapped concurrently into the address space of more than one process .
这个 shared memory 对象就是,一个可以被同时映射到多个进程地址空间中的内存对象(实例)。
Associate part of that memory or the whole memory with the address space of the calling process.
接下来要做的事情就是,把部分或是全部共享内存空间关联到发出请求共享内存映射进程的地址空间中.
The operating system looks for a big enough memory address range in the calling process' address space and
marks that address range as a special range.
操作系统会在所有申请使用共享内存的地址空间中搜索一个足够大的内存空间,并且将这段“共享”的起始表示为
特殊地址区域。
Changes in that address range are automatically seen by other process that also have mapped the same shared memory object.
发生变化的地址区域会自动的对其他申请使用相同的共享内存的进程所见
Header
所需引入的头文件
To manage shared memory , you just need to include the following header.
管理共享内存,你应该将下面的这个头文件引入到你的代码中
#include
Creating shared memroy segments
创建共享内存段
As we've mentioned we have to use the shared_memory_object class to create , open and destory shared memory segments that
can be mapped by several process.
正如我们之前所提到过的,我们通过 shared_memory_object 类来实现 创建,打开和销毁 要映射到多个进程中的共享内存段
We can specify the access mode of that shared memory object( read only or read-write) , just as if it was a file :
可以通过设定访问共享内存对象的访问模式(访问模式有:只读,读写),就像是把这个共享内存当做文件一样看待
//------------------------------
Create a shared memory segment . Throws if already created
下面的代码用来创建一个共享内存段,如果这个共享内存段已经被创建,排除相应的错误信息
using boost::interprocess ;
shared_memory_object shm_obj ( create_only , // only create
"shared_memory" , // name of the shared memory
read_write // read-write mode
) ;
//----------------------------------
To open or create a shared memory segment:
以 打开-创建 的模式来访问一个共享内存段
using boost::interprocess ;
shared_memory_object shm_obj (
open_or_create , // this means if the shared memory not exist create one
"shared_memory" , // this means the name of the shared memory we gonna to create/open
read_only ) ;
//-----------------------------------------------------
To only open a shared memory segment. Throws if does not exist :
下面的代码仅用来打开一个共享内存段,如果该指定的共享内存段不存在的话,抛出相关的错误
using boost::interprocess ;
shared_memory_object shm_obj
( open_only , // only open the shared memory segment
"shared_memory" , // name of the shared memory segment
read_write ) ; // we can read from or write in the shared memory segment
//--------------------------------------------------------------------------
When a shared memory object is created, its size is 0 .
当共享内存对象被创建的时候,共享内存中的所有数据数值均为 0.
To set the size of the shared memory,the user must use the truncate function call , in a shared memory that has been opened with
read-write attributes:
可以通过调用 truncate 方法来设定共享内存空间的大小,不过前提必须是,该共享内存的访问模式是读-写模式
shm_obj.truncate(100000) ;
As shared memory has kernel or filesystem persistetnce , the user must explicitly destory it .
因为共享内存有着在内核与文件系统中的持久性,所以使用者必须以显示的方式来销毁该共享内存对象.
The remove operation might fail returning false if the shared memory does not exist,the file is open if the file is
still memory mapped by other processes :
如果共享内存对象本身并不存在或是该共享空间中存放有来自于其他进程中映射进去的文件数据的话,
调用 remove 方法来销毁共享空间对象的话将会失败.
using boost::interprocess ;
shared_memory_object::remove ("shared_memory") ;
For more details regarding shared_memory_object see the boost::interprocess::shared_memory_object class reference.
如果想知道 shared_object_object 更多的信息的话,可以查看
Mapping Shared Memory Segments
共享内存段的映射
Once created or opened, a process just has to map the shared memory object in the process' address space.
共享内存段一经创建或打开,进程只需要将共享内存对象映射到自己的地址空间中便可以(对它进行使用了).
The user can map the whols shared memory or just part of it .
使用者既可以将全部的也可将部分的共享内存映射到自己的地址空间中
The mapping process is done using the mapped_region class.
通过调用 mapped_regsion 这个类来完成映射过程
The class represents a memory regsion that has beed mapped from a shared memory or from other devices
that have also mapping capabilities(for example, files).
这个 class 抽象描述的是来自于共享空间或是其他具有被映射属性的对象(,比如说文件) 中的一段内存区域。
A mapped_region can be created from any memory_mappable object and as you might imagine , shared_memory_object is a memory_mappable object.
映射域可以从任何你能够想到的可映射对象中被创建出来,共享空间对象本身就是一个可映射对象
using boost::interprocess ;
std::size_t ShmSize - ...
// Map the seconde half of the memory
mapped_region region
(shm , // Memory-mappable object ; 可映射实体对象
read_write , // Access mode ; 映射之后映射区域访问的方式
ShmSize/2 , // Offset from the beginning of shm ; 设定映射实体中映射的起始点
ShmSize - ShmSize/2 // Length of the region ; 映射区域的长度
) ;
// Get the address of the region
// 获取刚刚通过映射得到 映射区域 的地址
region.get_address () ;
// Get the size of the region
// 获取 映射区域 的长度
region.get_size () ;
The user can specify the offset from the mappable object where the mapped region should start and the size of the mapped region.
使用者可以通过(上述方法中的参数来)设定作用在可映射对象的 offset 和 size 的数值来指定映射域的起始位置和映射域的大小
If no offset or size is specified, the whole mappable object ( in this case, shared memory ) is mapped.
如果没有特殊指定 offset 或是 size 的数值的话,那么映射的是全部的可映射对象(在我们这篇文章中可映射对象是共享内存)
If the offset is specified , but no the size , the mapped region covers from the offset until the end of the mappable object.
如果指定偏移量 offset ,但没有指定长度 size 的话,映射域将会涵盖可映射对象中从偏移量开始的剩余全部空间
For more details regarding mapped_region see the
想要进一步获取使用 mapped_region 更详细的信息,可以参考链接
A Simple Example
一个简单的例子
Let's see a simple example of shared memory use. 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.
在服务进程操作结束之后,客户进程打开共享内存,并且将其映射到自己的地址空间中,检查该共享内存中的数值是否是初始化时传入的值
//===================================================
-
#include <boost/interprocess/shared_memory_object.hpp>
-
#include <boost/interprocess/mapped_region.hpp>
-
#include <cstring>
-
#include <cstdlib>
-
#include <string>
-
-
int main(int argc, char *argv[])
-
{
-
using namespace boost::interprocess;
-
-
if(argc == 1){ //Parent process
-
//Remove shared memory on construction and destruction
-
// 这个地方创建了一个结构体,结构体中的构造析构方法中均调用了 shared_memory_object
-
// 类中的删除名为 "MySharedMemory" 的共享内存对象
-
// 这么做的作用是,在程序一开始的时候,便会 通过结构体的构造方法 创建结构体,从而调用了删除共享内存
-
// (防止原本存在没有释放同名的共享内存会造成后续代码中创建同名已经存在的共享内存而出现错误)
-
//---- 而析构方法是用来在程序结束的时候,结构体也会调用系统方法来再次调用删除共享内存的方法
-
// 作用是,将本程序中创建的名为 "MySharedMemory" 的共享内存空间进行释放
-
-
struct shm_remove
-
{
-
shm_remove() { shared_memory_object::remove("MySharedMemory"); }
-
~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
-
} remover;
-
-
//Create a shared memory object.
-
// 首先,创建一个共享内存对象 shm
-
shared_memory_object shm (create_only, "MySharedMemory", read_write);
-
-
//Set size
-
// 通过调用 truncate 方法来设定创建的共享内存对象的容量
-
shm.truncate(1000);
-
-
//Map the whole shared memory in this process
-
// 然后通过调用 mapped_region 方法以读写的方式将共享内存空间映射到
-
// 可以直接使用的 mapped_region 中,通过下面的参数可知是将共享内存空间全部映射到映射域对象中
-
mapped_region region(shm, read_write);
-
-
//Write all the memory to 1
-
// 这个地方的方法使用挺有意思,region.get_address () 相当于获取了共享空间的起始地址,
-
// 刚好符合 memset (void* , int , size_t) 的 void* 的参数类型 ; region.get_size() 刚好满足了 memset 中的第三个参数的要求
-
// 所以,下面的方法调用结束之后,映射域中的全部的 1000 个字节的数值均是数值 1
-
std::memset(region.get_address(), 1, region.get_size());
-
-
//Launch child process
-
// 这里的方法调用也可以学习一下,我们启动当前程序的时候,(假设程序已经被编译成名为 test 的二进制可执行文件)
-
// 那么运行它的时候用 ./test 便可以让程序运行,在程序中的 argc == 1 argv[0] = ./test
-
// 这个也就是上述程序描述中的 server 进程
-
// 那么如何创建一个客户进程呢? 答案便是通过下面的调用, 首先是创建了一个 "./test" 的 string
-
// 然后,在将其与 "child" 拼接成为 "./test child" 并将这段命令交给 system 来执行, 使用 system 执行就相当于
-
// 程序员自己重新在控制台中输入一次 ./test child 的命令一样,但是这次
-
// 不同的是, argv[0] = ./test argv[1] = child 且 argc = 2 != 1 所以,再次进入到程序中的时候,走得并不是 if 分支
-
// 而是 else 分支,我们所要做的就是在 else 分支上面写上 client 进程需要处理的代码即可
-
-
std::string s(argv[0]); s += " child ";
-
if(0 != std::system(s.c_str()))
-
return 1;
-
}
-
else{
-
// client 进程将要执行的代码
-
-
//Open already created shared memory object.
-
// 首先以只读模式打开 server 进程创建的共享内存对象
-
shared_memory_object shm (open_only, "MySharedMemory", read_only);
-
-
//Map the whole shared memory in this process
-
// 接下来将打开的共享内存对象以只读的方式全部映射到映射域中
-
mapped_region region(shm, read_only);
-
-
//Check that memory was initialized to 1
-
// 使用映射域对象的 get_address () 方法来获取映射域的起始地址,
-
// 并将其转换为 char* 交付给char* mem
-
// 然后通过 mem 逐个访问映射域中的数值是否是 server 进程写入的数值 1
-
char *mem = static_cast<char*>(region.get_address());
-
for(std::size_t i = 0; i < region.get_size(); ++i)
-
if(*mem++ != 1)
-
return 1; //Error checking memory
-
}
-
return 0;
-
}
Emulation for systems without shared memory objects
(boost::interprocess) 在不支持共享内存对象系统中的仿真技术
Boost.Interprocess provides portable shared memory in terms of POSIX semantics . Some operating systems don't support shared memory as defined by
POSIX :
Boost.Interprocess 中提供遵照 POSIX 标准具有可移植性的共享内存语义规则. 但有些操作系统并不支持一句 POSIX 标准定义的共享内存 :
Windows operating systems provide shared memory using memory backed by the paging file but the lifetime semantics are different from the ones
defined by POSIX( see for more information).
Windows 操作系统通过使用通过备份于分页文件中的数据来提供共享内存这一功能,这样一来便使得共享内存的生命周期(要长于)与 POSIX 标准中
共享空间的生命周期产生了差异(按照译者的理解便是,分页文件对应的共享内存生存周期是属于该操作系统中的文件系统的,而POSIX 标准共享内存
的生存周期应该是进程级别的) (有关 Windows 操作系统共享内存更详细的信息请看这一链接)
Some UNIX systems don't fully support POSIX shared memory objects at all .
(不仅是 Windows ) 一些 UNIX 系统也并不是全部支持 POSIX 中定义的共享内存对象准则的
In those platforms, shared memory is emulated with mapped file created in a "boost_interprocess" folder created in a temporary files directory .
在这些平台上,共享内存是通过映射文件技术被仿真实现的,这些映射文件在缓存文件目录下的名为"boost_interprocess" 文件夹下被创建.
In Windows platforms , if "Common AppData" key is present in the registry , "boost_interprocess" folder is created in that directory ( in XP usually
"C:\Documents and Setting\All User\Application Data" and in Vista "C:\ProgramData").
在 Windows 平台上,如果 "Common AppData" 的钥匙存放于注册表中,"boost_interprocess" 文件夹将会被创建在该路径下( 在Windows XP 操作系统中
这个路径是 "C:\Documents and Setting\All User\Application Data" 在 Vist 操作系统平台下路径是 “C:\ProgramData”).
For Windows platforms without that registry key and Unix systems,shared memory is created in the system temporary files directory("/tmp" or similar) .
没有注册钥匙的 Windows 平台和 Unix 操作系统平台上,共享系统通常被创建在存放系统临时文件的路径下面( 例如 "/tmp" 或是类似的文件路径下面)
Because of this emulation, shared memory has filesystem lifetime in some of thoese systems.
正是由于使用仿真技术才使得共享内存在某些系统中拥有了同文件系统一样的生命周期长度.
Removing shared memory
移除共享内存
shared_memory_object provides a static remove function to remove a shared memory objects.
共享内存对象(shared_memory_object) 这个类中提供了用于将共享内存对象实例移除的静态方法
This function can fail if the shared memory objects does not exist or it's opened by another process.
如果要移除的共享内存实例压根不存在或是这个共享内存在其他进程中被打开,调用移除方法将会失败
Note that this function is similar to the standard C int remove ( const char *path) function .
需要注意一点这个移除方法与标准 C 库函数中 int remove ( const char *path ) 方法原理上十分的相似
In UNIX systems, shared_memory_object::remove calls shm_unlike:
在 Unix 操作系统中,共享内存对象移除操作类似于断开与共享内存对象的链接
The function will remove the name of the shared memory object named by the string pointed to by name.
(断开与共享内存的链接)这种方法将会移除通过 string 类型作为名称所对应的共享内存对象 .
<这句话的意思就是说,在执行断开共享内存对象的链接之后,系统将会根据 string 类型的共享内存对象名称找到
该共享对象实体,然后断开名称与实体之间的联系,相当于是在 map> 对中,通过 key 找到 value ,
然后将 key 对应的 value 实体进行清空, 但是 key 值不被删除 >
If one or more references to the shared memory object exists when is unlinked the name will be removed before the
function returns , but the removal of the memory object contents will be postponed until all open and map references
to the shared memory object have been removed.
如果共享内存被映射到了多个进程中的时候执行断开链接操作的话,将会把该映射的内存区域在方法返回之前从进程地址空间中移除,
但是被移除的这个内存对象中所存放的内容的清空操作将会推迟,直到所有访问该共享内存的进程都断开了与该共享内存的映射关系为止
Even if the object continues to exist after the last function call , reuse of the name will subsequently cause the creation of a
boost::interprocess::shared_memory_object instance to behave as if no shared memory object of this name exists ( that is ,
trying to open an object with that name will fail and an object of the same name can be created again ).
即便在最后一个方法调用之后(进程会断开与共享内存对象链接)这个共享内存对象仍旧是存在于系统中的,但是继续通过该
共享内存的对象的名称来重用该共享内存对象,会相应的产生这样的一个后果:系统在调用 boost::interprocess::shared_memory_object
实例对象之后,会被告知系统中并不存在该名称的共享内存。(事实上就是如果试图使用一个断开链接的共享内存对象的话,将会收到错误消息,
随后系统会创建一个同名的共享对象出来)
In Windows operating systems , current version supports an usually acceptable emulation of the UNIX unlink behaviour: the file is renamed with
a random name and marked as to be deleted when the last open handle is closed.
在 Windows 操作系统中,当前的主流版本是支持 Unix 中通过断开连接操作来删除共享空间这一仿真方法的: 在该方法中,断开链接的共享内存对象(文件)
将会被重新分配一个随机名称,并且当最后一个打开该共享内存(文件)的句柄被关闭时,该共享内存对象(文件) 标示为删除状态.
to be continue
阅读(3772) | 评论(2) | 转发(0) |