我的公司正在开发一个"花式"在Windows 7下运行的USB大容量存储设备用于处理客户端实际存储介质读写的大容量存储客户端驱动程序是用C ++编写的。我们遇到的问题是写入速度非常非常慢。比预期慢约30倍。我们使用WriteFile()调用将数据块写入存储介质(特别是物理驱动器' PhysicalDrive2'),因为它们是从主机设备接收的。我在许多其他论坛中已经阅读过,人们使用WriteFile()特别是在Windows 7上使用WriteFile()的写入速度非常慢。所以我试图弄清楚我是否正在使用最佳方法和函数调用此特定任务。
以下是两段代码。一个用于LockVolume()函数,在初始化期间由程序调用一次,实际上只是卸载卷。另一块代码是WriteSector(),用于在USB Client控制器驱动程序接收到物理驱动器时将实际数据写入物理驱动器。我希望有人可以对我可能做错的事情有所了解,或者就更好的方法提出建议。
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()的原始调用,并将其替换为现在传递给WriteFile的OVERLAPPED结构。在我做出这个改变之后,我现在得到了错误状态"堆叠变量' MyOverLappedStructure'被腐蚀了#34;下面是我的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()函数的一部分,以便在调用WriteFile()之后检查IO是否处于挂起状态。如果是这样,我调用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(2/10/2015)
所以代码现在正在使用Ben的输入。上面的代码已经过修改以反映这些更改。我需要提一下,直到今天下午,我的所有测试都在客户端的存储介质是USB闪存驱动器的情况下完成。我已经改变了,所以客户端现在写入附加的SSD。通过USB闪存驱动器设置,我可以通过USB连接将数据写入客户端的速度现在几乎与客户端SBC可以将同一文件直接从其自身传输到存储介质的速度相同(没有主机)连接的)。但是,现在使用SSD,情况并非如此。我正在使用的测试文件是34MB,直接从客户端SBC传输到SSD需要2.5秒。通过USB从主机到客户端需要2.5分钟。除了更改卷号和物理驱动器号之外,没有对代码进行任何其他更改
答案 0 :(得分:5)
您不应为每个被覆盖的扇区调用CreateFile
和CloseHandle
。 CreateFile
是非常昂贵的操作,必须进行安全检查(评估组成员资格,步行SID等)。
打开手柄一次,将其传递给WriteFile
多次,然后关闭一次。这意味着将_dsk
参数从卷路径更改为HANDLE。
您可能还希望失去对SetFilePointer
的调用,而是使用OVERLAPPED
结构,这样您就可以提供写入调用的位置。 (除非您使用FILE_FLAG_OVERLAPPED
,否则操作不会 重叠,但非重叠I / O会考虑OVERLAPPED结构中的位置信息。)