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)中返回零。进一步的调用返回非零统计信息。显然,系统启动时有大量的磁盘 Activity ,但没有计入。因此,看起来重启后第一次调用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
diskperf.exe -Y添加此 key ,diskperf.exe -N删除它。

我试图将此项直接添加/删除到注册表中。

如果 key 不存在并且DeviceIoControl不起作用(在运行diskperf.exe -N之后),我将如下所示添加此 key :
reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1

,然后DeviceIoControl立即开始工作。

如果 key 存在并且DeviceIoControl有效(在我运行diskperf.exe -Y之后),我将删除该 key 如下:
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl

然后DeviceIoControl继续工作,返回的统计信息不断增长。直到重新启动。
diskperf.exe除了更改注册表值之外,还必须执行其他操作,例如强制刷新或刷新注册表。就我而言,我关心启用这些计数器,并且似乎可以通过简单添加注册表项来工作。

最佳答案

您可以使用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将不再具有弹出磁盘之前的状态统计信息。

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

09-25 21:46