Моя компания разрабатывает «причудливое» запоминающее устройство USB, работающее под управлением Windows 7. Драйвер клиента запоминающего устройства, который выполняет чтение и запись на фактический носитель на стороне клиента, пишется на C ++. Проблема в том, что скорость записи очень и очень низкая. Примерно в 30 раз медленнее, чем ожидалось. Мы используем вызовы WriteFile () для записи блоков данных на носитель данных (в частности, на физический диск PhysicalDrive2), когда они получены от хост-устройства. Я читал на многих других форумах, что люди испытывают очень медленную скорость записи при использовании WriteFile (), особенно в Windows 7. Поэтому я пытаюсь выяснить, использую ли я лучшие методы и вызовы функций для этой конкретной задачи.
Ниже приведены два блока кода. Одна для функции LockVolume (), которая вызывается программой один раз во время инициализации и фактически просто отключает том. Другой блок кода — WriteSector (), который используется для записи фактических данных на физический диск, когда они получены драйвером контроллера USB-клиента. Я надеюсь, что кто-то может пролить некоторый свет на то, что я могу делать неправильно, или предоставить предложения по лучшему способу реализации этого.
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 (10/10/2015)
Поэтому я включил предложения, сделанные Беном Фойгтом, и обнаружил, что вызов CreateFile и CloseHandle только один раз (вместо того, чтобы каждый раз записывать данные на диск) значительно улучшил скорость записи. Увеличение на 80%. Даже при таком увеличении скорость записи была намного ниже ожидаемой. Примерно в 6 раз медленнее. Затем я включил его другое предложенное изменение, которое включало в себя устранение исходного вызова SetFilePointer () и замену его структурой OVERLAPPED, которая теперь передается в 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 (10/10/2015)
Поэтому я исключил FILE_FLAG_OVERLAPPED из вызова CreateFile () для комментария Бена. Я также изменил часть функции WriteSector (), чтобы включить проверку, чтобы увидеть, ожидает ли IO после вызова WriteFile (). Если это так, я вызываю функцию WaitForSingleObject (), которая ожидает неопределенно долго до завершения операции ввода-вывода. Наконец, я вызываю CloseHandle () для структуры OVERLAPPED hEvent. Даже с этими изменениями я все еще получаю ошибку «стек вокруг переменной« 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 (10/10/2015)
Таким образом, код теперь работает с входами Бена. Код выше был изменен, чтобы отразить эти изменения. Я должен упомянуть, что до сегодняшнего дня все мои тесты проводились там, где носителем на стороне клиента была флешка. С тех пор я изменил это, поэтому клиент теперь пишет на подключенный SSD. При настройке флэш-накопителя USB скорость, с которой я могу записывать данные клиенту по USB-соединению, теперь практически идентична скорости, с которой клиент SBC может передавать тот же файл непосредственно с себя на носитель данных (без хоста). связано). Однако при использовании SSD это не так. Используемый мной тестовый файл размером 34 МБ занимает 2,5 секунды при передаче напрямую из клиентского SBC в SSD. Это займет 2,5 минуты от хоста к клиенту через USB. За исключением изменения буквы тома и номера физического диска, в код не было внесено никаких других изменений.
Вы не должны звонить CreateFile
а также CloseHandle
для каждого сектора перезаписано. CreateFile
это очень дорогостоящая операция, требующая проверки безопасности (оценка членства в группах, обход SID и т. д.).
Откройте ручку один раз, передайте ее WriteFile
много раз, и закройте его один раз. Это означает изменение вашего _dsk
Параметр из пути тома к РУЧКЕ.
Вы, вероятно, также хотите потерять вызов SetFilePointer
и использовать OVERLAPPED
вместо структуры, которая позволяет вам указать позицию для записи как часть вызова записи. (Операция не будет быть перекрываются, если вы не используете FILE_FLAG_OVERLAPPED
, но не перекрывающийся ввод-вывод учитывает информацию о положении в структуре OVERLAPPED).