在托管 .NET 代码中分配非托管内存

发布于 2024-07-12 12:07:19 字数 422 浏览 9 评论 0原文

您好,我有一个非托管函数,它获取由 malloc 分配的一块内存,并稍后以异步方式释放它。 我想将其包装到托管包装器中。 下面的代码可以吗?

void managed_fx (byte data __gc[], size_t size)
{
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unmanaged_data, size);
}

Hi I have a unmanaged function that takes a chunk of memory allocated by malloc and deallocates it later on in async manner. I want to wrap it into managed wrapper. Is following code OK?

void managed_fx (byte data __gc[], size_t size)
{
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unmanaged_data, size);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

你的往事 2024-07-19 12:07:19

我的 MC++ 有点生疏,但我认为 __pin 会固定源变量,直到“pinned_data”超出范围(至少,这就是 C++/CLI 中等效的 pin_ptr 所做的)。 通常,您应该尽快取消固定它,即出于性能原因,您不应在同一范围内调用 fx。

在这种情况下是否有必要固定?

是的。 您想要从非托管代码访问托管内存。

gc 可以在 memcpy 之前释放它吗

吗?否。对数据有强引用,所以gc不会收集它。 然而,它可能会收集一些其他对象,并在压缩步骤中移动内存中的数据。 那么malloc就会访问错误的内存区域。 固定可以防止这种情况发生,但代价是为 GC 进行额外的簿记(这就是为什么您应该尽快取消固定对象)。

即使在托管代码中使用了非托管内存,malloc 是否也会分配该内存?

是的。

My MC++ is a little rusty, but I think __pin pins the source variable until "pinned_data" goes out of scope (at least, that's what the equivalent pin_ptr in C++/CLI does). You should generally un-pin it as soon as possible, i.e. you shouldn't call fx in the same scope for performance reasons.

Is pinning necessary at all in this case?

Yes. You want to access managed memory from unmanaged code.

Can gc deallocate it before memcpy

No. There is a strong reference to data, so the gc won't collect it. It might however collect some other object, and move data in memory in the compacting step. Then malloc would access the wrong memory area. Pinning prevents this, at the cost of additional bookkeeping for the GC (which is why you should un-pin objects as soon as possible).

Does malloc allocate an unmanaged memory even though it's used in managed code?

Yes.

爱她像谁 2024-07-19 12:07:19

你需要测试你的 malloc 它实际上已经分配了你的指针。
不了解 C++/CLI,所以我不知道你的 pin 是否可以工作。 您不需要使用编组库来保证在复制内存时固定内存吗?

you need to test your malloc that it actually has allocated your pointer.
Don't know C++/CLI so I don't know if your pin will work. Don't you need to use the Marshalling library to guarantee that the memory gets pinned while you copy it?

鹤仙姿 2024-07-19 12:07:19

有关如何分配的信息,请阅读本文非托管数据。 查看 Marshall 类。 也许会有一些方法可以替代你的 Managed_fx 函数。

Read this for information on how to allocate unmanaged data. Take a look of Marshall class. Maybe there would be some method which can replace your managed_fx function.

变身佩奇 2024-07-19 12:07:19

好吧,让我们变得更简单:

void managed_fx (byte data __gc[])
{
     //  Pin the data
     byte __pin *pinned_data = &data [0];

     //  Copy data to the unmanaged buffer.
     void *unmanaged_data = malloc (data.Length);
     assert (unmanaged_data);
     memcpy (unmanaged_data, (byte*) pinned_data, data.Length);

     //  Forward the call
     fx (unamanged_data, data.Length);
 }

不过,我还有几个问题:

  1. 在这种情况下是否有必要固定? gc 可以在 memcpy 发生之前释放它吗?
  2. 即使在托管代码中使用了非托管内存,malloc 是否也会分配该内存?

Ok, let's make it even simpler:

void managed_fx (byte data __gc[])
{
     //  Pin the data
     byte __pin *pinned_data = &data [0];

     //  Copy data to the unmanaged buffer.
     void *unmanaged_data = malloc (data.Length);
     assert (unmanaged_data);
     memcpy (unmanaged_data, (byte*) pinned_data, data.Length);

     //  Forward the call
     fx (unamanged_data, data.Length);
 }

Still, I have couple of questions:

  1. Is pinning necessary at all in this case? Can gc deallocate it before memcpy happens?
  2. Does malloc allocate an unmanaged memory even though it's used in managed code?
一场信仰旅途 2024-07-19 12:07:19

  1. 假设数据数组至少有同样大的大小。 这是一个可能等待发生的错误

  2. 不检查 malloc() 的结果

  3. 无论如何,您可能不需要这样做,只需传递固定字节指针即可没问题(除非调用的 fx() 函数以某种方式更改了数据,在这种情况下可能需要复制)。 根据稍后释放的内存,这很可能是不可能的。

    无论如何,

考虑到这一点,这里有一个修复版本

void managed_fx (byte data __gc[], size_t size)
{
    if (data.Length < size)
        throw gcnew System.IndexOutOfRangeException(
            "the unmanaged buffer is not big enough");
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    if (unmanaged_data == NULL) 
        throw gcnew OutOfMemoryException(
            "could not allocate memory for the unmanaged buffer");
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unamanged_data, size);
}

来回答进一步的问题:

固定对于执行 memcpy 是必要的。 它不会阻止 GC 运行时删除内存(技术上确实如此,但只是保留对其的引用),它会阻止内存在内存中移动。 由于 memcpy 是一个非托管函数 - 它无法处理它正在操作的在内存中移动的指针。
malloc 完全是非托管的(尽管运行时库可以自由使用托管堆的保留区域来执行此操作)。

对于“代码”是托管的还是非托管的,源 C++/CLI 中没有明显的区别。 编译时,某些部分将是 CIL 中间代码,其他部分将是纯本机代码,重要的是使用的变量是托管的还是非托管的。 托管引用(任何带有 ^ 的内容)只能由本身托管的代码操作/使用。 这意味着如果运行时改变了托管对象在内存中的位置,则它可以自由地改变所使用的底层指针的值。 它以对托管函数安全的方式执行此操作(当周围的世界发生变化时,它们会暂停)。
由于存在许多有用的函数,但不知道如何处理此类托管引用,您最终可能需要使用指向它们的简单指针。
由于此操作是不安全的(如果 GC 启动移动引用),因此固定指针的存在是为了使此操作安全且简单,因为它既需要一个指针,又在该指针的生命周期内阻止运行时移动指向的变量。

You are

  1. assuming that the data array is at least as big a size. This is a possible bug waiting to happen

  2. Not checking the result of malloc()

  3. You may not need to do this anyway, simply passing the pinned byte pointer should be fine (unless the called fx() function in some way changes the data in which case the copy is perhaps a requirement). Based on the memory being freed later it is likely that this is not a possibility though.

With that in mind here is a fixed up version

void managed_fx (byte data __gc[], size_t size)
{
    if (data.Length < size)
        throw gcnew System.IndexOutOfRangeException(
            "the unmanaged buffer is not big enough");
    //  Pin the data
    byte __pin *pinned_data = &data [0];

    //  Copy data to the unmanaged buffer.
    void *unmanaged_data = malloc (size);
    if (unmanaged_data == NULL) 
        throw gcnew OutOfMemoryException(
            "could not allocate memory for the unmanaged buffer");
    memcpy (unmanaged_data, (byte*) pinned_data, size);

    //  Forward the call
    fx (unamanged_data, size);
}

To answer the further questions:

The pinning is necessary to do the memcpy. It does not prevent the deletion of the memory by the GC runtime (well technically it does but so does simply holding of a reference to it) it prevents it moving in memory. Since memcpy is an unmanaged function - it cannot handle the pointers it is manipulating being moved about in memory.
malloc is entirely unmanaged (though the runtime libraries are free to use reserved areas of the managed heap to do this).

There is no clear distinction in source C++/CLI as to whether 'code' is managed or unmanaged. On compilation some parts will be CIL intermediate code, others plain native code the important thing is whether the variables used are managed or unmanaged. A managed reference (anything with the ^) can only be manipulated/used by code which is itself managed. This means the runtime is free to alter the values of the underlying pointers used as it sees fit if it alters the location of the managed object in memory. It does this in a way that is safe to managed functions (they are paused while the world is changed around them).
Since many useful functions exist with no knowledge of how to handle such managed references you may end up needing to take a simple pointer to them.
Since this operation is unsafe (if the GC kicks in a moves the reference about) the pinning pointer exists to make this safe and simple to do since it both takes a pointer and for the life of that pointer prevents the runtime moving the pointed to variable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文