C++ 如何获取全局Windows I/O统计信息?
有一个WinAPI函数,它提供给定进程的所有I/O操作的详细信息:自进程启动以来的读/写操作数和读/写字节数。任务管理器最有可能使用此功能显示以下数字:C++ 如何获取全局Windows I/O统计信息?,c++,windows,winapi,C++,Windows,Winapi,有一个WinAPI函数,它提供给定进程的所有I/O操作的详细信息:自进程启动以来的读/写操作数和读/写字节数。任务管理器最有可能使用此功能显示以下数字: 是否有一种相对简单的方法可以获得相同或相似的统计数据,但对于自启动以来的整个系统 请注意,它与枚举所有当前进程并汇总GetProcessIoCounters的结果不同,因为有些进程会启动、运行一段时间并完成。当我调用GetProcessIoCounters时,这样的进程已经不存在了,但我想知道系统的总体I/O 我打算每隔一小时左右收集这些统
是否有一种相对简单的方法可以获得相同或相似的统计数据,但对于自启动以来的整个系统 请注意,它与枚举所有当前进程并汇总
GetProcessIoCounters
的结果不同,因为有些进程会启动、运行一段时间并完成。当我调用GetProcessIoCounters
时,这样的进程已经不存在了,但我想知道系统的总体I/O
我打算每隔一小时左右收集这些统计数据,并将它们记录到数据库中,以便将来进行分析和帮助调试
我正在寻找一种在Windows XP上工作而不使用WMI的方法(我们使用了一种大大减少的嵌入式Windows XP),但是如果这种方法只适用于Windows的更高版本,请共享它。最终它将是有用的
更新
我尝试了作者建议的DeviceIoControl(IOCTL\u DISK\u PERFORMANCE)
方法
我必须跑去让它工作。我甚至不需要重新启动,但是没有它,DeviceIoControl
就失败了,GetLastError=31
(连接到系统的设备没有运行。)DeviceIoControl
在重新启动后继续工作,无需再次运行diskperf.exe-Y
,但是重新启动后对DeviceIoControl
的第一次调用在所有字段(BytesRead、BytesWrite、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
diskperf.exe-Y
添加此键,diskperf.exe-N
将其删除
我试图将此项直接添加/删除到注册表中
如果该键不存在且
DeviceIoControl
不起作用(在我运行diskperf.exe-N
之后),我会像这样添加此键:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl
#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";
}
,然后设备控制立即开始工作
如果该键存在并且DeviceIoControl
工作(在我运行diskperf.exe-Y
之后),并且我像这样删除该键:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl
#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";
}
然后,DeviceIoControl
继续工作,返回的统计信息不断增长。直到重新启动
diskperf.exe
除了更改注册表值外,还必须执行其他操作,例如强制刷新注册表。在我的例子中,我关心的是启用这些计数器,它似乎可以通过简单地添加注册表项来工作。您可以使用DeviceIoControl一次从一个磁盘获取数据,如下所示:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl /t REG_DWORD /d 1
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\PartMgr" /v EnableCounterForIoctl
#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: 3,956,736
Bytes written: 0
要获取系统中当前可见的所有驱动器的数据,请添加对GetLogicalDrives
或GetLogicalDriveStrings
的调用,并在循环中这样调用代码,但为每次调用填充适当的驱动器号
但是,这仍然不能保证是系统启动后的所有数据。例如,如果弹出一个可移动磁盘,则有关该驱动器读写内容的信息将丢失。如果将其重新插入,则只能获取自上次插入后的读/写数据
不过,我不确定您是否能够做得更好,至少不需要做大量额外的工作来定期收集数据并跟踪磁盘何时弹出和插入等等。当磁盘被弹出时,我怀疑Windows几乎丢弃了关于该驱动器的所有统计信息,因此,如果以后重新插入该驱动器,Windows将不再有关于在弹出之前对其所做操作的统计信息
同样,如果磁盘已弹出,因此当前不可见,则无法打开它,因此也无法检索有关该驱动器的任何统计信息。谢谢!在我的情况下,我没有可移动磁盘,所以这不是一个问题。唯一的一个小问题是文档中的这句话:控制代码启用提供磁盘性能信息的性能计数器。还有禁用性能计数器的
IOCTL\u DISK\u PERFORMANCE\u OFF
控制代码。我来看看它是怎么工作的。也许为了获得有意义的结果,我必须在重新启动后立即调用DeviceIoControl(IOCTL\u DISK\u PERFORMANCE)
,以开始收集统计数据。@VladimirBaranov:是的,我看到了——但至少在一些快速测试中,它似乎产生了如上所示的合理结果。我测试了DeviceIoControl(IOCTL\u DISK\u PERFORMANCE)的方式
有效。请参阅问题中的更新。你知道diskperf.exe-Y做什么以及如何在我的程序中做同样的事情吗?它看起来像是在注册表项HKEY\U LOCAL\U MACHINE\SYSTEM\CurrentControlSet\Services\PartMgr中添加一个值“EnableCounterForIoctl”=dword:00000001