我正在寻找关于“破解”Mono(实际上也是.NET)的一些建议。
背景:作为 Isis2 库 (Isis2.codeplex.com) 的一部分,我希望支持在具有正确硬件类型(Infiband NIC)的机器上对内存映射文件进行非常快速的“零复制”复制,并尽可能减少复制带有 UDP 的标准以太网。所以设置是这样的:我们有一组进程 {A,B....} 都链接到 Isis2,并且某个成员,可能是 A,有一个很大的内存映射文件,称之为 F,并请求 Isis2将 F 复制到 B、D、G 和 X 上。该库将非常有效且非常快速地完成此操作,即使许多并发发起者大量使用。我们的想法是将其提供给运行大数据应用程序的 HPC 和云开发人员。
现在,Isis2 在 .NET 上用 C# 编码,并通过 Mono 交叉编译到 Linux。.NET 和 Mono 都是托管的,所以他们都不想让我做零拷贝网络 I/O——正常的模型是“将你的数据复制到托管的 byte[] 对象中,然后使用 SendTo 或 SendAsync 发送。接收,同样的处理:接收或 ReceiveAsync 到一个 byte[] 对象,然后复制到文件中的目标位置。” 这将比硬件可以承受的速度慢。
事实证明,在 .NET 上,我可以绕过正常的内存保护。我构建了自己的映射文件包装器(实际上基于哥伦比亚研究人员几年前发布的一个)。我拉入 Win32Kernel.dll 库,然后使用 Win32 方法映射我的文件,启动套接字发送和接收调用等。通过一些黑客攻击,我可以通过这种方式模仿 .NET 异步 I/O,我最终一些相当干净且完全用 C# 编码的东西,没有任何 .NET 甚至将其识别为不安全的代码。我将我的映射文件视为一个大的非托管字节数组,避免了所有不必要的复制。显然我会保护我的 Isis2 用户的所有这些;他们不会知道的。
现在我们解决了我的问题的症结所在:在 Linux 上,我显然无法加载 Win32 内核 dll,因为它不存在。所以我需要使用核心 Linux O/S 调用来实现一些基本功能: fmap() 调用将映射我的文件。Linux 也有自己的异步 I/O 形式:对于 Infiniband,我将使用 Mellanox 的 Verbs 库,对于 UDP,我将使用原始 IP 发送和完成时的信号(“中断”)。丑陋,但我想我可以让它工作。再次,我将尝试将所有这些包装起来,使其看起来尽可能像标准异步 Windows 异步 I/O,以便 Isis2 本身的代码清洁,并且我将对最终用户隐藏整个非托管、不安全的混乱。
由于我将一次发送一个千兆字节左右的数据块,因此一个关键目标是按顺序发送的数据最好按照我发布异步接收的顺序接收。显然我不得不担心不可靠的通信(导致东西最终被丢弃,然后我必须复制)。但是,如果没有任何内容被丢弃,我希望我发送的第 n 个块最终位于第 n 个接收区域......
所以这是我的问题:有人已经这样做了吗?有人对 Mono 如何实现 .NET 大量使用的异步 I/O 调用有任何提示吗?我大概应该这样做。有没有人对如何以最小的痛苦做到这一点有任何建议?
还有一个问题:Win32 仅限于 2Gb 的映射文件。云系统通常会运行 Win64。关于如何最大限度地提高互操作性,同时允许运行 Win64 的人充分利用 Win64 的任何建议?(一种O/S反射问题……)