c++ - 在Windows 7上将原始数据写入硬盘(PhysicalDrive)的最快方法

标签 c++ performance windows-7 writefile

我公司正在开发一种在Windows 7下运行的“高档” USB Mass Storage Device。用于处理对客户端实际存储介质的读写的Mass Storage Client Driver用C++编写。我们遇到的问题是非常非常慢的写入速度。比预期慢30倍左右。当从主机设备接收到数据块时,我们正在使用对WriteFile()的调用将数据块写入存储介质(特别是物理驱动器'PhysicalDrive2')。我在许多其他论坛上都读到,人们使用WriteFile()的写入速度非常慢,尤其是在Windows 7上。因此,我试图确定我是否针对此特定任务使用了最佳方法和函数调用。

以下是两个代码块。一个用于LockVolume()函数,该函数在初始化期间被程序调用一次,实际上只是卸载该卷。另一个代码块是WriteSector(),当USB客户端 Controller 驱动程序接收到实际数据时,该代码用于将实际数据写入物理驱动器。我希望有人可以阐明我可能做错了什么,或者提供一些建议来提出更好的实现方法。

    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()的原始调用,并将其替换为OVERLAPPED结构,该结构现在传递给WriteFile。进行更改后,我现在得到错误提示,指出“变量'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(2/10/2015)

因此,根据Ben的注释,我从CreateFile()调用中消除了FILE_FLAG_OVERLAPPED。我还修改了WriteSector()函数的一部分,以包括检查以查看IO是否在调用WriteFile()之后挂起。如果是这样,我调用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(2015年2月10日)

因此,该代码现在可以与Ben的输入配合使用。上面的代码已被修改以反射(reflect)这些更改。我需要提到的是,直到今天下午,我所有的测试都在客户端存储介质是USB闪存驱动器的情况下完成。此后,我对此进行了更改,因此客户端现在可以写入附加的SSD。通过USB闪存驱动器设置,我现在可以通过USB连接将数据写入客户端的速度实际上与客户端SBC可以将相同文件直接从其自身传输到存储介质(无需主机)的速度相同。连接的)。但是,由于现在正在使用SSD,事实并非如此。从客户端SBC直接传输到SSD时,我正在使用的34MB测试文件需要2.5秒。通过USB从主机到客户端需要2.5分钟。除了更改卷号和物理驱动器号外,未对代码进行任何其他更改

最佳答案

您不应为每个被覆盖的扇区调用CreateFileCloseHandleCreateFile是一项非常昂贵的操作,必须进行安全检查(评估组成员身份,步行SID等)。

打开一次手柄,将其多次传递给WriteFile,然后关闭一次。这意味着将_dsk参数从卷路径更改为HANDLE。

您可能还想丢失对SetFilePointer的调用,而改为使用OVERLAPPED结构,这使您可以在write调用中提供要写入的位置。 (除非使用FILE_FLAG_OVERLAPPED,否则操作不会重叠,但是非重叠的I / O会遵守OVERLAPPED结构中的位置信息)。

关于c++ - 在Windows 7上将原始数据写入硬盘(PhysicalDrive)的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28375925/

相关文章:

visual-studio - Visual Studio 2013 Ultimate 无法安装 Windows 7 SP1

windows-7 - Windows 7 任务栏是新的通知区域吗?

c++ - 拥有可变数量的模板参数的最佳方法是什么?

c++ - 如何从最新的 SkeletalViewer 获取骨骼关节的 (x, y, z) 坐标? (C++)

C++:对临时的常量引用

c# - 我可以通过将 SQL 命令重构为 C# 类来提高性能吗?

.net - 当唯一的区别在于未执行的代码路径时,为什么性能会有所不同?

android - 在 ffmpeg 中读取 JPEG

java - 尝试捕捉性能 Java

windows-7 - 重新启动时 Windows 服务不自动启动