我的公司正在开发一个运行在Windows 7下的“花式”USB海量存储设备。负责处理客户端实际存储媒体的读写的海量存储客户端驱动程序正在用C++编写。我们遇到的问题是写的速度非常非常慢。比预期慢了大约30倍。当从主机设备接收到数据块时,我们使用对WriteFile()的调用将数据块写入存储介质(特别是物理驱动器'PhysicalDrive2')。我在许多其他论坛上读到,人们使用WriteFile()编写的速度非常慢,尤其是在Windows7上。因此,我试图弄清楚我是否在为这个特定的任务使用最好的方法和函数调用。
下面是两段代码。一个用于LockVolume()函数,该函数在初始化过程中由程序调用一次,实际上只是卸载卷。另一个代码块是WriteSector(),它用于在used控制器驱动程序接收到实际数据时将实际数据写入物理驱动器。我希望有人能弄清楚我可能做错了什么,或者给出更好的实施方法的建议。
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 (2015年10月2日)
因此,我采纳了Ben提出的建议,发现只调用CreateFile和CloseHandle一次(而不是每次我们想要向驱动器写入数据时)都大大提高了写入速度。增加80%。即使有了这样的增长,写入速度仍然比预期慢得多。大约慢了6倍。因此,我合并了他建议的另一个更改,包括删除对SetFilePointer()的原始调用,并将其替换为现在传递给WriteFile的和重叠的结构。在我做了这个更改之后,我现在得到和错误的状态是“变量'MyOverLappedStructure‘周围的堆栈被破坏了”。下面是我的SectorWrite函数的更新版本以及新的Disk_GetHandle()函数,它获得物理驱动器的初始句柄。另外,在调用Disk_LockVolume()之后,我仍然调用Disk_GetHandle()一次。但是,我修改了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)
因此,我从FILE_FLAG_OVERLAPPED的评论中删除了CreateFile()调用的内容。我还修改了WriteSector()函数的一部分,以包括一个检查,以查看在调用WriteFile()之后IO是否挂起。如果是这样的话,我将调用WaitForSingleObject(),它将无限期地等待IO操作完成。最后,我在重叠结构CloseHandle()上调用hEvent。即使进行了这些更改,我仍然会得到错误“变量' osWrite‘周围的堆栈已损坏”,其中osWrite是重叠的结构。下面是演示这些更改的代码片段。
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年10月2日)
因此,代码现在与Ben的输入一起工作。对上述代码进行了修改,以反映这些更改。我需要提到的是,直到今天下午,我所有的测试都是在客户端的存储介质是USB闪存的情况下完成的。我已经改变了这一点,所以客户现在写到一个附加的SSD。通过USB闪存驱动器设置,我可以通过USB连接向客户端写入数据的速度现在几乎与客户机SBC将同一文件直接从其自身传输到存储介质(没有主机连接)的速度相同。然而,由于目前使用的是SSD,情况并非如此。我使用的测试文件是34 SBC,直接从客户端SBC传输到SSD需要2.5秒。它需要2.5分钟从主机到客户端通过USB。除了更改卷号和物理驱动器号之外,没有对代码进行任何其他更改。
发布于 2015-02-06 23:24:04
您不应该为每个覆盖的扇区调用CreateFile
和CloseHandle
。CreateFile
是一种非常昂贵的操作,需要进行安全检查(评估组成员身份、散步小岛屿发展中国家等等)。
打开句柄一次,将其传递给WriteFile
多次,然后关闭一次。这意味着将_dsk
参数从卷路径更改为句柄。
您可能还希望放弃对SetFilePointer
的调用,而使用一个OVERLAPPED
结构,它允许您作为写调用的一部分提供要写入的位置。(除非使用FILE_FLAG_OVERLAPPED
,否则操作不会重叠,但不重叠的I/O会尊重重叠结构中的位置信息)。
https://stackoverflow.com/questions/28375925
复制相似问题