我正在使用C#直接从磁盘读取并且对kernel32的ReadFile方法进行了调整。我注意到,对于较大的读取(目前只读取单个块),缓冲区大小超出范围。
有人知道读缓冲区的最大大小吗?
如果是这样的话,当我有多余的内存时,限制缓冲区大小的目的是什么?想要读入?我理解缓冲和保持较小内存占用的概念,但为什么小尺寸强加给我们?也许只是旧版Win32 API的人工制品?
编辑:
从Marshal.GetLastWin32Error()
收到的错误是“值不在预期范围内。”
我收到此错误之前的上限是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());
}
}
}
}
提前致谢。
答案 0 :(得分:1)
没有这样的限制。你错了。
显然,您受地址空间的限制,并且要求缓冲区是连续的虚拟内存块。在32位系统上,每个进程只能处理2GB的虚拟内存。更不用说你将无法分配2GB的连续内存块。
但这些是一般限制。 ReadFile
API很乐意读入您可以分配的大缓冲区。
您声称已达到8KB的限制但我刚刚使用WriteFile
和ReadFile
成功编写并读取了1GB文件。显然你有一些问题,但它不是你认为的那样。如果你可以显示剩下的代码,特别是那些调用你的p / invoke,那么我相信它会变得很明显。
现在您已发布完整代码,我们可以看到问题所在。您不是在读取文件,而是在读取物理磁盘。我现在看到你的意思是“直接从磁盘上阅读”,但我认为你可能会更具体一点!
无论如何,我不知道这里发生了什么的细节,但问题显然不是ReadFile
本身,而是你的句柄是物理磁盘而不是文件。
CreateFile
州的文档:
卷包含一个或多个已安装的文件系统。即使在CreateFile中未指定非高速缓存选项,也可以根据特定文件系统的判断将卷句柄打开为非高速缓存。您应该假设所有Microsoft文件系统都将未处理的卷句柄打开。对文件的非高速缓存I / O的限制也适用于卷。
即使数据未被缓存,文件系统也可能需要或不需要缓冲区对齐。但是,如果在打开卷时指定了非高速缓存选项,则无论卷上的文件系统如何,都会强制执行缓冲区对齐。建议在所有文件系统上将卷句柄打开为非高速缓存,并遵循非高速缓存的I / O限制。
我认为您应该考虑询问有关如何从物理磁盘读取的新问题。
答案 1 :(得分:1)
说到ReadFile(),它的行为和成功在很大程度上取决于文件的方式 用CreateFile()打开。只是为了给你一个正确的方向点思考这一点;当你调用CreateFile()读取缓冲区时,你通常不会想要FILE_FLAG_NO_BUFFERING,除非你准备好处理数据对齐和一些特定的硬件特定信息,尽管它可能只是更容易没有。
您的代码似乎没有这样做,但它是互操作,因此可能会有一个乱码值,但是.NET因为骚扰(?)内存而臭名昭着块,所以你可能想尝试通过互操作分配内存,保证内存块不会移动到任何地方。此外,我会尝试尽可能地减少互操作,当它最需要时似乎有点不可靠。相反,您可能会考虑编写一个DLL,它将使用您的代码播放更好并替代它。我在很多情况下已经完成了这个并且它工作得很好并且增加了不必编写的奖励相同的代码,只需在你喜欢的程序中重复使用它...至于你的问题的根本原因,我自己没有能够复制它,我尝试过的最大文件大约800MB它打开,读取,关闭好的...你可以提供源代码,以便其他人(或一个非常冗长的可执行文件)进行测试,看看其他人是否有同样的问题吗?无论如何希望这有助于某人。
CreateFile() ReadFile() from MSDN 2011 documentation
(备注部分第9和13段)
File Buffering
缓冲区大小和扇区对齐的限制
进一步说明建议使用VirtualAlloc()来获取对齐的内存块
答案 2 :(得分:0)
不确定要读入的缓冲区的最大大小,但是缓冲区大小的原因是,在函数内部可以进行检查,因此不会在结束时写入数据。缓冲。如果函数没有输入缓冲区大小的概念,它可以写入缓冲区的末尾,导致缓冲区溢出。这将是一个严重的安全漏洞。
答案 3 :(得分:0)
当ReadFile
正在运行时,内核必须锁定缓冲区。它必须这样做,否则可能会发生恶意事件(当它试图复制数据时,页面无法存在,或者更糟糕的是...... DMA进入它)。
每个应用程序的工作集都有限制,您也可以configure programmatically。如果您(或代表您的内核)尝试锁定的内存超出工作集限制所允许的内存,则会失败。你不能锁定超过你的最小工作集大小,默认值相当小(IIRC类似16MB)。
请注意,“最大工作集大小”并不是您的应用程序允许使用多少内存(这将是悲剧性的)。这是一个关于何时属于您的流程的页面被视为“可能被其他人想要记忆而被分页”的数字。
所有这一切背后的意图是确保系统继续使用许多同时运行的程序,使用未知量的内存。