首页 > 解决方案 > 使用 AcquirePointer 时处理 SafeMemoryMappedViewHandle 的正确方法是什么?

问题描述

我有一些代码可以打开一个内存映射文件并ReadOnlySlice<T>为一些解析操作公开一个。简化类代码如下:

public MemoryMappedViewAccessor Accessor { get; }
public SafeMemoryMappedViewHandle Handle { get; }
public byte* Memory;
private long _size;

public Parser(MemoryMappedFile mappedFile, long offset, long size)
{
    _size = size;
    Accessor = mappedFile.CreateViewAccessor(offset, _size, MemoryMappedFileAccess.Read);
    Handle = Accessor.SafeMemoryMappedViewHandle;
    unsafe
    {
        Handle.AcquirePointer(ref Memory);
    }
}

public ReadOnlySlice<T> GetSpan<T>(int offset, int size)
{
    return new ReadOnlySpan<T>(chunk.Memory, _size).Slice(offset, size);
}

/* other functions exposing various Slice<T> over this */

显然,这需要我的班级来实现IDisposable,但我不确定不安全资源和安全资源之间的界限在哪里。我发现的一个参考资料说这SafeMemoryMappedViewHandle是一个托管资源,应该这样处理,但它没有提到它是如何改变的ReleasePointer,或者是否ReleasePointer应该在之前调用Dispose

在我看来,以下两种Dispose模式是我的选择:

选项 1 - 将所有内容视为托管

private bool _isDisposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_isDisposed)
    {
        if (disposing)
        {
            Accessor.Dispose();
            Handle.ReleasePointer(); // is this even needed?
            Handle.Dispose();
        }

        _isDisposed = true;
    }
}

public void Dispose()
{
    Dispose(true);
}

选项 2 - 将指针释放视为非托管

private bool _isDisposed = false;

protected virtual void Dispose(bool disposing)
{
    if (!_isDisposed)
    {
        if (disposing)
        {
            Accessor.Dispose();
        }

        Handle.ReleasePointer();
        Handle.Dispose();

        _isDisposed = true;
    }
}

~FileChunk()
{
    Dispose(false);
}

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

在这两种情况下,我都会在我的解析逻辑中添加一个检查以抛出一个ObjectDisposedExceptionif_isDisposed为真,以避免 UAF 错误和内存损坏。

当从 a 中暴露不安全的指针时,以下哪一个是正确的处置模式SafeMemoryMappedViewHandle?此外,是否值得Memory在处置期间将该字段设置为空?

标签: c#disposememory-mapped-filesunsafe

解决方案


推荐阅读