我公司正在开发一种在Windows 7下运行的“高档” USB Mass Storage Device。用于处理对客户端实际存储介质的读写的Mass Storage Client Driver用C++编写。我们遇到的问题是非常非常慢的写入速度。比预期慢30倍左右。当从主机设备接收到数据块时,我们正在使用对WriteFile()的调用将数据块写入存储介质(特别是物理驱动器'PhysicalDrive2')。我在许多其他论坛上都读到,人们使用WriteFile()的写入速度非常慢,尤其是在Windows 7上。因此,我试图确定我是否针对此特定任务使用了最佳方法和函数调用。
以下是两个代码块。一个用于LockVolume()函数,该函数在初始化期间被程序调用一次,实际上只是卸载该卷。另一个代码块是WriteSector(),当USB客户端 Controller 驱动程序接收到实际数据时,该代码用于将实际数据写入物理驱动器。我希望有人可以阐明我可能做错了什么,或者提供一些建议来提出更好的实现方法。
short WriteSector
(LPCWSTR _dsk, // disk to access
char *&_buff, // buffer containing data to be stored
unsigned int _nsect, // sector number, starting with 0
ULONG Blocks
)
{
DWORD bytesWritten;
wchar_t errMsg[256];
//attempt to get a handle to the specified volume or physical drive
HANDLE hDisk = CreateFile(_dsk, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//make sure we have a handle to the specified volume or physical drive
if(hDisk==INVALID_HANDLE_VALUE)
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
OutputDebugString(errMsg);
printf("Error attempting to get a handle to the device! (%s)\n", errMsg);
goto exit;
}
// set pointer to the sector on the disk that we want to write to
SetFilePointer(hDisk, (_nsect * SIZE_OF_BLOCK), 0, FILE_BEGIN);
//write the data
if (!WriteFile(hDisk, _buff, (Blocks * SIZE_OF_BLOCK), &bytesWritten, NULL))
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("WriteFile failed! (%s)\n", errMsg);
goto exit;
}
exit:
CloseHandle(hDisk);
writeMutex.unlock();
return 0;
}
UINT Disk_LockVolume(LPCWSTR _dsk)
{
HANDLE hVol;
LPWSTR errMsg;
DWORD status;
bool success = false;
//now try to get a handle to the specified volume so we can write to it
hVol = CreateFile(_dsk, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//check to see if we were able to obtain a handle to the volume
if( hVol == INVALID_HANDLE_VALUE )
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("Disk_LockVolume() - CreateFile failed (%s)\n", errMsg);
goto exit;
}
// now lock volume
if (!DeviceIoControl(hVol, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &status, NULL))
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("Disk_LockVolume() - Error attempting to lock device! (%s)\n", errMsg);
goto exit;
}
//dismount the device
if (!DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &status, NULL))
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("Disk_LockVolume() - Error attempting to dismount volume. (%s)\n", errMsg);
goto exit;
}
exit:
CloseHandle(hVol);
return 1;
}
编辑#1(2/10/2015)
因此,我结合了Ben Voigt提出的建议,发现仅调用一次CreateFile和CloseHandle(而不是每次都希望将数据写入驱动器)可以显着提高写入速度。增加80%。即使增加了速度,写入速度仍然比预期的要慢得多。慢6倍左右。因此,我然后结合了他的其他建议更改,包括消除了对SetFilePointer()的原始调用,并将其替换为OVERLAPPED结构,该结构现在传递给WriteFile。进行更改后,我现在得到错误提示,指出“变量'MyOverLappedStructure'周围的堆栈已损坏”。下面是我的SectorWrite函数的更新版本以及新的Disk_GetHandle()函数,该函数获取物理驱动器的初始句柄。另外,在我调用Disk_GetHandle()之后,我仍然一次调用Disk_LockVolume()。但是,我已经修改了Disk_LockVolume()函数,以使卷的句柄(在这种情况下)不会在函数末尾关闭。最终,在关闭物理驱动器上的手柄之前,将在程序结束时将其关闭。对此新错误的任何想法将不胜感激。哦,FILE_FLAG_NO_BUFFERING对我所看到的性能没有影响。
UINT WriteSector(HANDLE hWriteDisk, PBYTE Buf, ULONG Lba, ULONG Blocks)
{
DWORD bytesWritten;
LPTSTR errMsg = "";
//setup overlapped structure to tell WriteFile function where to write the data
OVERLAPPED overlapped_structure;
memset(&overlapped_structure, 0, (Blocks * SIZE_OF_BLOCK));
overlapped_structure.Offset = (Lba * SIZE_OF_BLOCK);
overlapped_structure.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//write the data
if (!WriteFile(hWriteDisk, Buf, (Blocks * SIZE_OF_BLOCK), &bytesWritten, &overlapped_structure))
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("WriteSector() - WriteFile failed (%s)\n", errMsg);
}
if (bytesWritten != (Blocks * SIZE_OF_BLOCK))
{
printf("WriteSector() - Bytes written did not equal the number of bytes to be written\n");
return 0;
}
else
{
return Blocks;
}
}
HANDLE Disk_GetHandle(UINT Lun)
{
HANDLE hVol;
LPTSTR errMsg = "";
bool success = false;
//now try to get a handle to the specified volume so we can write to it
hVol = CreateFile(MassStorageDisk[Lun].PhysicalDisk, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, 0);
//check to see if we were able to obtain a handle to the volume
if( hVol == INVALID_HANDLE_VALUE )
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("Disk_WriteData() - CreateFile failed (%s)\n", errMsg);
}
return hVol;
}
编辑#2(2/10/2015)
因此,根据Ben的注释,我从CreateFile()调用中消除了FILE_FLAG_OVERLAPPED。我还修改了WriteSector()函数的一部分,以包括检查以查看IO是否在调用WriteFile()之后挂起。如果是这样,我调用WaitForSingleObject(),它无限期地等待直到IO操作完成。最后,我在OVERLAPPED结构hEvent上调用CloseHandle()。即使进行了这些更改,我仍然会收到错误“变量'osWrite'周围的堆栈已损坏”,其中osWrite是OVERLAPPED结构。下面是说明更改的代码段。
OVERLAPPED osWrite;
memset(&osWrite, 0, (Blocks * SIZE_OF_BLOCK));
osWrite.Offset = (Lba * SIZE_OF_BLOCK);
osWrite.hEvent = 0;
//write the data
if (!WriteFile(hWriteDisk, Buf, (Blocks * SIZE_OF_BLOCK), &bytesWritten, &osWrite))
{
DWORD Errorcode = GetLastError();
if (Errorcode == ERROR_IO_PENDING)
{
WaitForSingleObject(osWrite.hEvent, INFINITE);
}
else
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errMsg, 255, NULL);
printf("WriteSector() - WriteFile failed (%s)\n", errMsg);
goto exit;
}
}
编辑#3(2015年2月10日)
因此,该代码现在可以与Ben的输入配合使用。上面的代码已被修改以反射(reflect)这些更改。我需要提到的是,直到今天下午,我所有的测试都在客户端存储介质是USB闪存驱动器的情况下完成。此后,我对此进行了更改,因此客户端现在可以写入附加的SSD。通过USB闪存驱动器设置,我现在可以通过USB连接将数据写入客户端的速度实际上与客户端SBC可以将相同文件直接从其自身传输到存储介质(无需主机)的速度相同。连接的)。但是,由于现在正在使用SSD,事实并非如此。从客户端SBC直接传输到SSD时,我正在使用的34MB测试文件需要2.5秒。通过USB从主机到客户端需要2.5分钟。除了更改卷号和物理驱动器号外,未对代码进行任何其他更改
最佳答案
您不应为每个被覆盖的扇区调用CreateFile
和CloseHandle
。 CreateFile
是一项非常昂贵的操作,必须进行安全检查(评估组成员身份,步行SID等)。
打开一次手柄,将其多次传递给WriteFile
,然后关闭一次。这意味着将_dsk
参数从卷路径更改为HANDLE。
您可能还想丢失对SetFilePointer
的调用,而改为使用OVERLAPPED
结构,这使您可以在write调用中提供要写入的位置。 (除非使用FILE_FLAG_OVERLAPPED
,否则操作不会重叠,但是非重叠的I / O会遵守OVERLAPPED结构中的位置信息)。
关于c++ - 在Windows 7上将原始数据写入硬盘(PhysicalDrive)的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28375925/