AppDomain地址空间

16

首先,问题是:CLR规范是否保证在同一进程中的多个应用程序域中执行的代码将共享相同的地址空间?通过“共享地址空间”我指的是在一个应用程序域中分配内存的指针可以在托管同一进程的所有应用程序域中进行读写。

考虑这个自包含示例,它说明了这个问题:程序在一个单独的应用程序域中分配了一个Worker对象。 Worker为10,000个整数分配了一个内存块,并填充了数据。然后,程序跨应用程序域边界调用以获取指向分配块的指针,并验证它可以读取这10,000个项目中的每一个。

using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace crossapp {
    public class Worker : MarshalByRefObject {
        private readonly IntPtr myData;
        public const int DataLength = 10000;
        public Worker() {
            Console.Error.WriteLine(
                "Memory allocation happens in app domain '{0}'"
            ,   Assembly.GetExecutingAssembly().FullName
            );
            myData = Marshal.AllocHGlobal(sizeof(int) * DataLength);
            unsafe {
                var ptr = (int*) myData.ToPointer();
                for (var i = 0 ; i != DataLength ; i++) {
                    ptr[i] = 2*i + 1;
                }
            }
        }
        public IntPtr GetData() {
            return myData;
        }
    }
    class Program {
        static void Main() {
            var ad = AppDomain.CreateDomain("New domain");
            var wrk = (Worker)ad.CreateInstanceAndUnwrap(
                Assembly.GetExecutingAssembly().FullName
            ,   "crossapp.Worker"
            );
            var data = wrk.GetData();
            var badCount = 0;
            unsafe {
                var ptr = (int*)data.ToPointer();
                for (var i = 0 ; i != Worker.DataLength ; i++) {
                    var expect = 2*i + 1;
                    if (ptr[i] != expect) {
                        Console.Error.WriteLine(
                            "Mismatch in position {0}: {1} != {2}"
                        ,   i, expect, ptr[i]
                        );
                        badCount++;
                    }
                }
                if (badCount == 0) {
                    Console.Error.WriteLine(
                        "All {0} items have matched."
                    ,   Worker.DataLength
                    );
                } else {
                    Console.Error.WriteLine(
                        "Found {0} mismatches out of {1}."
                    ,   badCount
                    ,   Worker.DataLength
                    );
                }
            }
        }
    }
}

我尝试过很多次,每次都成功了。直觉上来说应该是行得通的:毕竟应用域在同一个进程中,因此它们必须共享同一虚拟地址空间。但是这感觉像是在利用 Microsoft 可能随时会取消的功能。CLR 规范中是否有确认或否认这种技巧的内容呢?如果我能证明它的合法性,那么这将是传递大量(以 GB 为单位)数据跨应用程序域边界的理想解决方案,且空间和时间开销都很小。


Marshal.AllocHGlobal文档中写道:从进程的非托管内存中分配内存。这似乎表明,只要应用程序域存在于同一进程中,以此方式创建的指针就是跨应用程序域安全的。 - Sam Axe
6
@downvoter 这个问题有什么不妥之处导致你点了反对? - Sergey Kalinichenko
2个回答

1
请看:谁能解释MarshalByRefObject的主要用途。 在您的情况下,您只传递了一个代理而不是实际对象,并且没有复制内存。
编辑:
  1. "合法性"不等于"技巧",你的黑客行为正在通过未在另一个AppDomain中调用数据来破坏AppDomains。运行时容器可能设置/更改安全限制,这可能会破坏你的应用程序,例如,这是否在IIS内运行?(在你的情况下,你不是访问对象而是内存,所以可能“不太糟糕”)。这是你在客户网站部署的产品吗?
  2. 我假设通过代理进行编组存在性能问题,因此您采用了IntPtr(去掉中间人)
  3. 各种“工作”AppDomains是否正在操作blob?如果是这样,我会担心最终内存会被破坏...因为你没有编组你的调用。
  4. 这是C#中创建的非托管内存。如果实际上blob是由非托管DLL分配的,则必须确保非托管DLL没有卸载。同样,如果您部署到IIS,则无法控制您的AppDomains的生命周期,IIS可以控制。这将破坏你的黑客行为。
  5. 是的,虚拟内存是每个进程而不是每个AppDomain,因此所有AppDomain共享相同的虚拟地址空间。
  6. 尽管我有保留,但它非常酷:)

我并没有建议要复制内存。我正在传递一个指针,它的工作方式就像指向相同的内存一样。我的问题是,是否有标准的部分保证了这将会发生? - Sergey Kalinichenko
1
你没有传递一个“指针”,而是传递了一个代理。它看起来很像CORBA的“模型”:你正在传递一个代理,该代理将命令编组到另一个AppDomain中的对象。 - Hezi
通过使用代理进行调用的是一个普通指针。查看第二个“unsafe”块的第一行,以了解它的使用方式。指针是否使用代理传递并不重要:一旦指针已经传递,我就通过该指针访问其余数据,绕过代理,并且它可以正常工作。我正在寻找证明这不是巧合的证据。 - Sergey Kalinichenko

-1

我没有直接的答案给你。MarshalByRefObject的存在可能表明使用了共享地址空间,但也可能不是。

你还可以研究一下内存映射文件


1
MarshalByRefObject 的文档在某些地方说“对象的成员不能在创建它们的应用程序域之外使用”,并且还说它们返回“代理”。这使我想到 Microsoft 可能进行了一些非平凡的映射,或者保留了将来进行一些非平凡的映射的权利。至于内存映射文件,它们比“普通”内存更重,因为它们必须跨进程边界工作。除此之外,没有文件:我需要传递指向瞬态数据块的指针。 - Sergey Kalinichenko
我没有阅读文档的那部分内容。至于内存映射文件,我认为您可以先固定内存,然后将指针写入mmf。但经过进一步思考,您可以只固定内存,然后使用现有的指针传递函数。 - Sam Axe

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