私の会社は、Windows 7 で動作する「派手な」USB 大容量記憶装置を開発しています。クライアント側で実際の記憶媒体の読み取りと書き込みを処理する大容量記憶装置クライアント ドライバーは、C++ で記述されています。私たちが抱えている問題は、書き込み速度が非常に遅いことです。予想よりも約 30 倍遅い。WriteFile() の呼び出しを使用して、ホスト デバイスから受信したデータ ブロックをストレージ メディア (具体的には物理ドライブ 'PhysicalDrive2') に書き込みます。私は他の多くのフォーラムで、特に Windows 7 で WriteFile() を使用して書き込み速度が非常に遅いという経験があることを読みました。そのため、この特定のタスクに最適なメソッドと関数呼び出しを使用しているかどうかを把握しようとしています。
以下に 2 つのコード ブロックを示します。1 つは、初期化中にプログラムによって 1 回呼び出され、実際にはボリュームをアンマウントするだけの LockVolume() 関数用です。もう 1 つのコード ブロックは 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(2015年2月10日)
そこで、Ben Voigt の提案を取り入れたところ、(ドライブにデータを書き込むたびにではなく) CreateFile と CloseHandle を 1 回だけ呼び出すと、書き込み速度が大幅に向上することがわかりました。80% 増加。その増加にもかかわらず、書き込み速度は予想よりもはるかに遅かった. 約6倍遅い。そこで、彼が提案した他の変更を取り入れました。これには、SetFilePointer() への元の呼び出しを削除し、それを、WriteFile に渡される OVERLAPPED 構造に置き換えることが含まれます。その変更を行った後、「変数 '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(2015年2月10日)
そのため、Ben のコメントに従って CreateFile() 呼び出しから FILE_FLAG_OVERLAPPED を削除しました。また、WriteSector() 関数の一部を変更して、WriteFile() の呼び出し後に IO が保留中かどうかを確認するチェックを含めました。その場合、IO 操作が完了するまで無期限に待機する WaitForSingleObject() を呼び出します。最後に、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 の入力を処理するようになりました。上記のコードは、これらの変更を反映するように変更されています。今日の午後まで、クライアント側のストレージ メディアが USB フラッシュ ドライブである場合にすべてのテストが行われたことに言及する必要があります。その後、クライアントが接続された SSD に書き込むように変更しました。USB フラッシュ ドライブの設定により、USB 接続を介してクライアントにデータを書き込むことができる速度は、クライアント SBC が同じファイルをそれ自体からストレージ メディアに直接転送できる速度と実質的に同じになりました (ホストを介さずに)接続されています)。しかし、現在SSDが使用されているため、これは当てはまりません。私が使用している 34MB のテスト ファイルは、クライアント SBC から SSD に直接転送すると 2.5 秒かかります。USB 経由でホストからクライアントまで 2.5 分かかります。