如何获取全局Windows I/O统计信息?

Vla*_*nov 3 c++ windows winapi

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

任务管理器


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

请注意,它与枚举所有当前进程并总结结果不同GetProcessIoCounters,因为有进程启动,运行一段时间并完成.当我调用GetProcessIoCounters这样的进程时不再存在,但我想知道系统的整体I/O.

我打算每小时收集一次这些统计数据并将其记录到数据库中以供将来分析并帮助调试.

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

更新

我尝试了Jerry CoffinDeviceIoControl(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 -Y和之后导出了整个注册表,diskperf.exe -N并寻找差异.我能找到的唯一区别是一键:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PartMgr
"EnableCounterForIoctl"=dword:00000001
Run Code Online (Sandbox Code Playgroud)

diskperf.exe -Y添加此密钥,diskperf.exe -N将其删除.

我试图将此密钥直接添加/删除到注册表中.


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

reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1
Run Code Online (Sandbox Code Playgroud)

,然后DeviceIoControl立即开始工作.

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

reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl
Run Code Online (Sandbox Code Playgroud)

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

diskperf.exe除了更改注册表值之外,还必须执行其他操作,例如强制刷新/刷新注册表.在我的情况下,我关心启用这些计数器,它似乎与简单添加注册表项一起使用.

Jer*_*fin 5

您可以使用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";
}
Run Code Online (Sandbox Code Playgroud)

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

Bytes read: 15,768,173,568
Bytes written: 22,370,663,424
Run Code Online (Sandbox Code Playgroud)

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

Bytes read: 3,956,736
Bytes written: 0
Run Code Online (Sandbox Code Playgroud)

要获取系统中当前可见的所有驱动器的数据,请添加对GetLogicalDrives或的调用GetLogicalDriveStrings,并在循环中调用此类代码,但为每个调用填写相应的驱动器号.

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

我完全不确定你能做多少比这更好,至少没有做大量的额外工作来定期收集数据并跟踪磁盘弹出和插入的时间等等. .当磁盘弹出时,我怀疑Windows几乎抛弃了有关该驱动器的所有统计信息,因此如果稍后重新插入,Windows将不再关于弹出之前对其执行的操作的统计信息.

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