【问题标题】:How to get global Windows I/O statistics?如何获取全局 Windows I/O 统计信息?
【发布时间】:2015-05-26 05:28:51
【问题描述】:

有一个 WinAPI 函数 GetProcessIoCounters 提供给定进程的所有 I/O 操作的详细信息:读/写操作数和自进程启动以来读/写的字节数。很可能任务管理器使用此功能来显示这些数字:


有没有一种相对简单的方法来获得相同或相似的统计数据,但对于整个系统,因为它开始?

请注意,它与枚举所有当前进程并总结GetProcessIoCounters的结果是不一样的,因为有进程启动,运行一段时间并结束。在我打电话给GetProcessIoCounters的时候,这样的进程已经不存在了,但是我想知道系统的整体I/O。

我打算每隔一小时左右收集一次这些统计数据并将它们记录到数据库中以供将来分析和帮助调试。

我正在寻找一种在没有 WMI 的情况下在 Windows XP 上工作的方法(我们使用显着缩减的 Windows XP Embedded),但如果这种方法仅适用于更高版本的 Windows,请分享。最终它会很有用。

更新

我尝试了Jerry Coffin建议的DeviceIoControl(IOCTL_DISK_PERFORMANCE)方法。

我必须运行diskperf.exe -Y 才能使其工作。我什至不必重新启动,但没有它DeviceIoControl 失败并出现GetLastError=31(连接到系统的设备无法运行。)DeviceIoControl 在重新启动后继续工作,无需再次运行diskperf.exe -Y ,但重新启动后对DeviceIoControl 的第一次调用在所有字段(BytesRead、BytesWritten、ReadCount、WriteCount)中返回零。进一步的调用返回非零统计数据。显然,系统启动时有一些实质性的磁盘活动,但没有计算在内。因此,看起来重启后对DeviceIoControl 的第一次调用确实启用/启动了计数器。

如果我运行diskperf.exe -N,那么DeviceIoControl 会立即停止工作,无需重新启动。当我运行diskperf.exe -Y 时,DeviceIoControl 又可以正常工作了。

现在的问题是:diskperf.exe -Y 做了什么以及如何在我的程序中做同样的事情?

更新 2

我在diskperf.exe -Ydiskperf.exe -N 之后导出了整个注册表并寻找不同之处。我能找到的唯一区别在于一个键:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PartMgr
"EnableCounterForIoctl"=dword:00000001

diskperf.exe -Y 添加此密钥,diskperf.exe -N 删除它。

我尝试将这个键直接添加/删除到注册表中。


如果密钥不存在并且DeviceIoControl 不起作用(在我运行diskperf.exe -N 之后),我会像这样添加这个密钥:

reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1

,然后DeviceIoControl 立即开始工作。

如果密钥存在并且DeviceIoControl 有效(在我运行diskperf.exe -Y 之后),我会像这样删除这个密钥:

reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl

然后DeviceIoControl 继续工作,返回的统计数据不断增长。直到重启。

diskperf.exe 除了更改注册表值之外,还必须执行其他操作,例如强制刷新/刷新注册表。就我而言,我关心启用这些计数器,并且它似乎可以通过简单地添加注册表项来工作。

【问题讨论】:

    标签: c++ windows winapi


    【解决方案1】:

    您可以使用 DeviceIoControl 一次获取一个磁盘的数据,如下所示:

    #include <windows.h>
    #include <iostream>
    
    int main() { 
        HANDLE dev = CreateFile("\\\\.\\C:", 
            FILE_READ_ATTRIBUTES, 
            FILE_SHARE_READ | FILE_SHARE_WRITE, 
            NULL, 
            OPEN_EXISTING, 
            0, 
            NULL);
    
        DISK_PERFORMANCE disk_info { };
        DWORD bytes;
    
        if (dev == INVALID_HANDLE_VALUE) {
            std::cerr << "Error opening disk\n";
            return 1;
        }
    
        if (!DeviceIoControl(dev, 
                IOCTL_DISK_PERFORMANCE, 
                NULL, 
                0, 
                &disk_info, 
                sizeof(disk_info), 
                &bytes, 
                NULL))
        {
            std::cerr << "Failure in DeviceIoControl\n";
            return 1;
        }
    
        std::cout.imbue(std::locale(""));
        std::cout << "Bytes read: " << disk_info.BytesRead.QuadPart << "\n";
        std::cout << "Bytes written: " << disk_info.BytesWritten.QuadPart << "\n";
    }
    

    例如,现在在我的机器上显示:

    Bytes read: 15,768,173,568
    Bytes written: 22,370,663,424
    

    在实验中,我得到的结果看起来很合理。例如,插入闪存驱动器并打开其中一些图片的预览后,我得到:

    Bytes read: 3,956,736
    Bytes written: 0
    

    要获取系统中当前可见的所有驱动器的数据,请添加对 GetLogicalDrivesGetLogicalDriveStrings 的调用,并在循环中调用类似这样的代码,但为每个调用填写适当的驱动器号。

    这仍然不能保证是自系统启动以来的所有数据。例如,如果您弹出可移动磁盘,则有关从该驱动器读取/写入该驱动器的信息会丢失。如果重新插入,您将只能获得自上次插入以来读取/写入内容的数据。

    我完全不确定你是否能做得比这更好,至少不需要做大量额外的工作来定期收集数据并跟踪磁盘何时弹出和插入等。当磁盘被弹出时,我怀疑 Windows 几乎会丢弃有关该驱动器的所有统计信息,因此如果稍后将其重新插入,Windows 将不再 提供有关在弹出之前对其进行了哪些操作的统计信息。

    同样,如果磁盘已被弹出,因此当前不可见,则无法将其打开以开始,因此也无法检索有关该驱动器的任何统计信息。

    【讨论】:

    • 谢谢!就我而言,我没有可移动磁盘,所以这不是问题。唯一令人担忧的是文档中的这句话:IOCTL_DISK_PERFORMANCE 控制代码 启用 提供磁盘性能信息的性能计数器。还有IOCTL_DISK_PERFORMANCE_OFF 控制代码禁用 性能计数器。我会检查它是如何工作的。也许为了获得有意义的结果,我必须在重启后立即致电 DeviceIoControl(IOCTL_DISK_PERFORMANCE) 以开始收集统计数据。
    • @VladimirBaranov:是的,我看到了——但至少在一些快速测试中,它似乎产生了如上所示的合理外观结果。
    • 我测试了DeviceIoControl(IOCTL_DISK_PERFORMANCE) 的工作原理。请查看问题中的更新。你知道diskperf.exe -Y 做了什么以及如何在我的程序中做同样的事情吗?
    • 看起来diskperf.exe -Y 将值"EnableCounterForIoctl"=dword:00000001 添加到注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PartMgr。感谢您展示 DeviceIoControl(IOCTL_DISK_PERFORMANCE) - 结果正是我所需要的。
    • @kayleeFrye_onDeck:它将std::cout 设置为使用千位分隔符格式化数字(或者适合您配置的区域设置,无论如何)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 2014-02-11
    • 2019-03-11
    • 2012-05-07
    • 1970-01-01
    相关资源
    最近更新 更多