ReadFile Kernel32最大缓冲区大小

本文关键字:缓冲区 Kernel32 ReadFile | 更新日期: 2023-09-27 18:10:18

我使用c#直接从磁盘读取并调用kernel32 ReadFile方法。我注意到,对于较大的读取(目前只读取单个块),缓冲区大小超出了范围。

有人知道这里读缓冲区的最大大小吗?

如果是这样的话,当我有多余的内存想要读取时,限制缓冲区大小的目的是什么?我理解缓冲和保持较小内存占用的概念,但是为什么我们必须使用较小的内存占用?也许只是一个旧的Win32 API的产物?

编辑:从Marshal.GetLastWin32Error()接收到的错误是"Value does not fall within expected range"。

在我收到此错误之前的上限是8192字节(8KB -因此我感到困惑)。

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace DiskRead
{
    class Program
    {
        public const uint GenericRead = 0x80000000;
        public const uint FileShareRead = 1;
        public const uint FileShareWrite = 2;
        public const uint OpenExisting = 3;
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
          uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
          uint dwFlagsAndAttributes, IntPtr hTemplateFile);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer,
           uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
        static void Main(string[] args)
        {
            string path = @"''.'PhysicalDrive0";
            IntPtr ptr = CreateFile(path, GenericRead, FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);
            SafeFileHandle handleValue = new SafeFileHandle(ptr, true);
            FileStream fileStream = new FileStream(handleValue, FileAccess.Read);
            const uint numberOfBytesToRead = 8193;
            uint bytesRead;
            byte[] buffer = new byte[numberOfBytesToRead];

            if (!ReadFile(handleValue.DangerousGetHandle(), buffer, numberOfBytesToRead, out bytesRead, IntPtr.Zero))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }
        }
    }
}

提前感谢。

ReadFile Kernel32最大缓冲区大小

没有这样的限制。你错了。

显然,您受到地址空间和缓冲区是一个连续的虚拟内存块的要求的限制。在32位系统上,每个进程只能寻址2GB的虚拟内存。更重要的是,您将无法分配2GB的连续内存块。

但是这些都是一般的限制。ReadFile API将高兴地读入您可以分配的尽可能大的缓冲区。

您声称已达到8KB的限制,但我刚刚使用WriteFileReadFile成功地写入和读取了1GB的文件。很明显你有问题,但不是你想的那样。如果你能展示剩下的代码,特别是调用你的p/invoke的代码,那么我相信它会变得很明显。


现在你已经发布了完整的代码,我们可以看到问题是什么。您不是在读取文件,而是在读取物理磁盘。我现在明白了,这就是你所说的"直接从磁盘读取",但我认为你可以更具体一点!

无论如何,我不知道这里发生了什么细节,但问题显然不是ReadFile本身,而是你的句柄是物理磁盘而不是文件。

CreateFile的文档说明:

卷包含一个或多个已挂载的文件系统。卷句柄可以根据特定文件系统的决定打开为非缓存,即使在CreateFile中没有指定非缓存选项。您应该假设所有Microsoft文件系统都以非缓存的方式打开卷句柄。对文件的非缓存I/O的限制也适用于卷。

文件系统可能需要也可能不需要缓冲区对齐,即使数据是非缓存的。但是,如果在打开卷时指定了非缓存选项,则无论卷上的文件系统是什么,都强制执行缓冲区对齐。建议在所有文件系统上以非缓存方式打开卷句柄,并遵循非缓存I/O限制。

我认为你应该考虑问一个关于如何从物理磁盘读取的新问题。

我不确定这是否对你有帮助,但它可能对这篇文章的其他读者有帮助:

  1. 当涉及到ReadFile()时,它的行为和成功在很大程度上取决于文件的形式使用CreateFile()打开。为了给你们一个正确的方向思考一下;当您调用CreateFile()来读取缓冲区时,通常不需要FILE_FLAG_NO_BUFFERING,除非您准备处理数据对齐和一些特定于硬件的信息,尽管不这样做可能会更容易。

  2. 你的代码似乎没有这样做,但它互操作,所以可能有一个乱码值通过某处,和。net是臭名昭著的骚扰(?)内存块,所以你可能想尝试通过互操作分配内存,以保证内存块不会移动到任何地方。此外,我会尽量减少互操作尽可能,它似乎是不可靠的,当它是最需要的。相反,您可能会考虑编写一个DLL,使与您的代码更好地发挥并与之互操作。我已经在很多情况下这样做了,它工作得很好,并且增加了不必重写相同代码的好处,只需在您喜欢的许多程序中重用它……至于你的问题的根本原因,我自己没能复制它,我试过的最大的文件大约是800MB,它可以打开,读取,关闭…您是否可以提供源代码,以便其他人(或非常详细的构建可执行文件)对其进行测试,以查看其他人是否有相同的问题?

  • CreateFile() ReadFile() from MSDN 2011文档
    (备注部分第9及13段)

  • 文件缓冲
    缓冲区大小和扇区对齐的限制进一步说明建议使用VirtualAlloc()来获得对齐的内存块

不确定要读取的缓冲区的最大大小,但是缓冲区大小的原因是,在函数内部可以进行检查,因此没有数据写入超过缓冲区的末尾。如果函数不知道输入缓冲区的大小,它可能会写过缓冲区的末尾,导致缓冲区溢出。这将是一个严重的安全漏洞。

ReadFile运行时,内核必须锁定缓冲区。它必须这样做,否则可能会发生坏事(当它试图复制数据时,页面可能不存在,或者更糟……DMA into it).

每个应用程序对其工作集都有限制,您也可以通过编程方式进行配置。如果您(或代表您的内核)试图锁定超过工作集限制的内存,则会失败。您不能锁定超过您的最小工作集大小,它默认是一个相当小的值(IIRC类似于16MB)。

注意,"最大工作集大小"并不是你的应用程序允许使用多少内存(那将是悲剧)。这是一个属于您进程的页面被认为"可能被换出,以防其他人需要内存"的数字。

这一切背后的意图是确保系统在许多程序并发运行的情况下保持工作,使用未知数量的内存