两个进程之间的共享内存(应用程序)

22

虽然类似的问题已经被提出过几次,但是我并没有找到有用的答案。

我想在两个进程之间(即两个不同的应用程序)共享一个内存,以便其中一个进程可以向该内存写入数据,而另一个进程可以读取该内存。

.NET中是否可能实现这一功能?如何实现?

谢谢。


复制:https://dev59.com/d3RC5IYBdhLWcg3wAcQ3 - Jørn Schou-Rode
1
但是在那个重复的问题中也没有好的答案。 - Tea Bee
重复的问题中有好的答案。您只是不喜欢它们所说的话。但在 .NET 中没有内置的共享内存方式,您可以使用 P-Invoke 或尝试一些其他的 IPC。 - Niki
1
这正是我想听到的,我一直认为这在目前的.NET中不可能实现(请查看我的问题),只是需要有人确认一下。谢谢你、nobugz和所有其他人。 - Tea Bee
5个回答

32

目前,.NET框架不支持Memory Mapped Files (内存映射文件)。然而,在.NET 4.0版本中,将引入System.IO.MemoryMappedFiles命名空间来支持这一功能。这项更新花费了很长时间,但是它的实现方式可能会让您不太满意。在MMFs中使用指针是必需的,因为共享内存是在特定地址上可用的。要共享一个值,您必须将其写入到特定地址。

然而,指针与托管内存模型根本不兼容。对象创建在一个垃圾回收堆中,垃圾收集器会根据需要移动它们以保持堆的紧凑性。在托管语言中,您有一个对这种对象的引用,也称为“跟踪句柄”。C/C++的等价物是指针,但是它带有附加功能。垃圾回收器始终可以找到并更新其值。CLR支持“pinning”的概念,它将引用转换为指针。它通过标记对象为不可移动来实现。但是,这并不能帮助通过MMF实现共享内存,因为对象在GC堆中被固定,而不是在MMF视图所在的虚拟内存地址中。

要使MMF正常工作,需要将对象从GC堆复制到共享内存中。这需要序列化。在.NET 4.0中,相关类名为MemoryMappedViewStream。您可能已经看出来,这与使用命名管道或套接字没有区别。从MMF中获取数据和传递数据需要同样的努力。MMF仅仅是稍微更加高效,因为底层缓冲区不在内核内存池中。

您可以打破规则并立即这么做。您可以P/Invoke CreateFileMapping、OpenFileMapping和MapViewOfFile函数以创建MMF。并使用unsafe关键字以便创建指针。您需要使用值类型(如结构体)来表示共享内存元素,或者使用Marshal类。


2
你假设“共享数据”必须直接共享托管对象。如果有人想分享一些“普通”数据呢? - devdimi
不确定“plain”是什么意思。但是你仍然不能直接影响值类型的地址,它们存在于堆栈或GC堆中。需要指针才能将它们写入MMF。 - Hans Passant
1
我认为他所说的“纯数据”,指的是一堆字节,你需要自己解密,他只是想编写代码来读/写内存映射文件。 - BlueTrin
请问您能否评论一下这个答案的时效性?是否有改进? - Basic
4
它仍然是最新的。 - Hans Passant

5

IPC

如果你谈论的是进程间通信(IPC),那么有几种可能性,比如使用tcp/udp套接字、邮槽、命名管道、内存映射文件、Windows消息等。

.Net还提供了一些更高级的IPC,如.Net RemotingWCF,它们使用了前面提到的技术。你可以在这里阅读更多相关信息。

Memory Mapped Files

如果你真的想共享一个内存块而不是进行通信,那么也许内存映射文件正是你所需要的。.Net 4.0有MemoryMappedFile为此提供支持。如果你不能或不想使用.Net 4.0,你可以像win32示例中的代码一样自己实现它。或者你可以尝试使用这个代码,或者看看是否可以使用在这里这里提到的MemoryMappedFileStream。又或者使用这个FileMap类。


我不想使用命名管道、WCF、远程调用等等,这就是为什么我指定了“共享内存”,在C++中建立两个应用程序之间的共享内存很容易,但我找不到在C#.net中实现它的方法。我正在使用.NET 2.0,有没有办法用它来解决这个问题?我不想使用第三方工具。 - Tea Bee

1

使用命名管道

这是一种机制,允许一个进程将顺序数据写入流中,另一个进程可以从中读取。

请注意,命名管道仅允许顺序读取。如果您需要使用更复杂的查询,您可能需要使用客户端-服务器架构。在这种情况下,读取进程使用网络套接字查询其他进程以获取信息。


我不想使用命名管道、WCF、远程调用等,因此我指定了 “共享内存”,在 c++ 中两个应用程序之间的共享内存很容易建立,但我找不到在 C#.net 中执行此操作的方法。 - Tea Bee
我没有看到过共享内存的直接实现,但是你可以很容易地通过使用套接字实现第三个进程来处理两个进程的读写。 - Adam Matan
2
我认为大家都没有理解重点。共享内存与通过任何机制同步对象在本质上是不同的。使用共享内存时,进程之间共享一致的状态。而使用管道/ WCF /等,则需要进行(反)序列化/传输过程,这在循环方面是昂贵的。当您希望一个进程直接操作另一个进程中的对象时(比如Chrome有多个进程渲染到同一个窗口),就会使用共享内存。直接绘制要比反复序列化位图便宜得多。 - Basic


0
如前所述,请使用内存映射文件。 .NET < 4的实现可在以下网址找到:http://github.com/tomasr/filemap/ 我已经使用过并且运行非常流畅。当在提权/未提权的进程之间共享时,您可能会遇到一些安全性问题-解决方案是在提权进程中初始化内存映射文件并相应地设置其安全属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接