3.跨进程共享内存:内存映射文件我们已经实现了跨线程和跨进程的共享资源访问同步。但是传递/接收消息还需要共享资源。对于线程来说,只需要声明一个类成员变量就可以了。但是对于跨进程来说,我们需要使用到 win32API提供的内存映射文件(MemoryMappedFiles,简称MMF)。使用 MMF和使用win32信号量差不多。我们需要先调用 CreateFileMapping方法来创建一个内存映射文件的句柄:
viewsource
print?
01 | [DllImport( "Kernel32.dll" ,EntryPoint= "CreateFileMapping" , |
02 | SetLastError= true ,CharSet=CharSet.Unicode)] |
03 | internal static extern IntPtrCreateFileMapping(
uint hFile,
|
04 | SecurityAttributeslpAttributes,
uint flProtect, |
05 | uint
dwMaximumSizeHigh, uint
dwMaximumSizeLow, string
lpName); |
07 | [DllImport( "Kernel32.dll" ,EntryPoint= "MapViewOfFile" , |
08 | SetLastError= true ,CharSet=CharSet.Unicode)] |
09 | internal static extern IntPtrMapViewOfFile(IntPtrhFileMappingObject,
|
10 | uint
dwDesiredAccess, uint
dwFileOffsetHigh, |
11 | uint
dwFileOffsetLow, uint
dwNumberOfBytesToMap); |
13 | [DllImport( "Kernel32.dll" ,EntryPoint= "UnmapViewOfFile" , |
14 | SetLastError= true ,CharSet=CharSet.Unicode)] |
15 | [ return
:MarshalAs(UnmanagedType.VariantBool)] |
16 | internal static extern bool UnmapViewOfFile(IntPtrlpBaseAddress); | viewsource
print?
01 | public static MemoryMappedFileCreateFile( string
name, |
02 | FileAccessaccess,
int size) |
05 | throw
new ArgumentException( "Sizemustnotbenegative" , "size" ); |
07 | IntPtrfileMapping=NTKernel.CreateFileMapping(0xFFFFFFFFu, null , |
08 | ( uint )access,0,( uint )size,name); |
09 | if (fileMapping==IntPtr.Zero) |
10 | throw
new MemoryMappingFailedException(); |
12 | return
new MemoryMappedFile(fileMapping,size,access); | 我们希望直接使用pagefile中的虚拟文件,所以我们用-1(0xFFFFFFFF) 来作为文件句柄来创建我们的内存映射文件句柄。我们也指定了必填的文件大小,以及相应的名称。这样其他进程就可以通过这个名称来同时访问该映射文件。创建了内存映射文件后,我们就可以映射这个文件不同的部分(通过偏移量和字节大小来指定)到我们的进程地址空间。我们通过 MapViewOfFile系统方法来指定:
viewsource
print?
01 | public MemoryMappedFileViewCreateView(
int offset,
int size, |
02 | MemoryMappedFileView.ViewAccessaccess) |
04 | if ( this .access==FileAccess.ReadOnly&&access==
|
05 | MemoryMappedFileView.ViewAccess.ReadWrite) |
06 | throw
new ArgumentException( |
07 | "Onlyreadaccesstoviewsallowedonfileswithoutwriteaccess" , |
10 | throw
new ArgumentException( "Offsetmustnotbenegative" , "size" ); |
12 | throw
new ArgumentException( "Sizemustnotbenegative" , "size" ); |
13 | IntPtrmappedView=NTKernel.MapViewOfFile(fileMapping, |
14 | ( uint )access,0,( uint )offset,( uint )size); |
15 | return
new MemoryMappedFileView(mappedView,size,access); | 在不安全的代码中,我们可以将返回的指针强制转换成我们指定的类型。尽管如此,我们不希望有不安全的代码存在,所以我们使用Marshal 类来从中读写我们的数据。偏移量参数是用来从哪里开始读写数据,相对于指定的映射视图的地址。
viewsource
print?
01 | public byte ReadByte( int
offset) |
03 | return
Marshal.ReadByte(mappedView,offset); |
05 | public void WriteByte( byte
data, int offset) |
07 | Marshal.WriteByte(mappedView,offset,data); |
10 | public int ReadInt32( int
offset) |
12 | return
Marshal.ReadInt32(mappedView,offset); |
14 | public void WriteInt32( int
data, int offset) |
16 | Marshal.WriteInt32(mappedView,offset,data); |
19 | public void ReadBytes( byte []data,
int offset) |
21 | for ( int
i=0;i<data.Length;i++) |
22 | data[i]=Marshal.ReadByte(mappedView,offset+i); |
24 | public void WriteBytes( byte []data,
int offset) |
26 | for ( int
i=0;i<data.Length;i++) |
27 | Marshal.WriteByte(mappedView,offset+i,data[i]); | 但是,我们希望读写整个对象树到文件中,所以我们需要支持自动进行序列化和反序列化的方法。
viewsource
print?
01 | public object ReadDeserialize( int
offset, int
length) |
03 | byte []binaryData=
new byte [length]; |
04 | ReadBytes(binaryData,offset); |
05 | System.Runtime.Serialization.Formatters.Binary.BinaryFormatterformatter |
06 | =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); |
07 | System.IO.MemoryStreamms=
new System.IO.MemoryStream( |
08 | binaryData,0,length, true , true ); |
09 | object
data=formatter.Deserialize(ms); |
13 | public void WriteSerialize( object
data, int offset,
int length) |
15 | System.Runtime.Serialization.Formatters.Binary.BinaryFormatterformatter |
16 | =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); |
17 | byte []binaryData=
new byte [length]; |
18 | System.IO.MemoryStreamms=
new System.IO.MemoryStream( |
19 | binaryData,0,length, true , true ); |
20 | formatter.Serialize(ms,data); |
23 | WriteBytes(binaryData,offset); | 请注意:对象序列化之后的大小不应该超过映射视图的大小。序列化之后的大小总是比对象本身占用的内存要大的。我没有试过直接将对象内存流绑定到映射视图,那样做应该也可以,甚至可能带来少量的性能提升。
| |