4

私の会社は、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 構造体 hEv​​ent で 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 分かかります。

4

1 に答える 1

5

上書きされたセクターごとに 呼び出す必要はCreateFileありません。セキュリティ チェック (グループ メンバーシップの評価、SID のウォークなど) を実行する必要がある非常にコストのかかる操作です。CloseHandleCreateFile

ハンドルを一度開き、WriteFile何度も渡し、一度閉じます。これは、_dskパラメーターをボリューム パスから HANDLE に変更することを意味します。

への呼び出しを失い、代わりに構造体SetFilePointerを使用しOVERLAPPEDて、書き込み呼び出しの一部として書き込む位置を指定することもできます。(を使用しない限り、操作はオーバーラップしませんがFILE_FLAG_OVERLAPPED、非オーバーラップ I/O は OVERLAPPED 構造体の位置情報を尊重します)。

于 2015-02-06T23:24:04.217 に答える