我需要观察 Windows 机器上的某些进程何时启动或停止。我目前正在接入 WMI 系统并每 5 秒查询一次,但这会导致 CPU 每 5 秒出现一次峰值,因为 WMI 是 WMI。有没有更好的方法来做到这一点?我可以列出正在运行的进程并通过 System.Diagnostics 命名空间将退出事件附加到它们,但是没有用于创建的事件处理程序。
4 回答
在我无法在退出/清理时正确分离事件的情况下,我在监听 WMI 事件时出现 CPU 峰值。您可能想要检查您是否没有“泄露”WMI 事件订阅。以防万一尽早脱离事件,并确保你总是这样做。
为了进一步说明,这是我的 PowerShell 书中的一个示例,它使用 PSEventing 库侦听 WMI 事件:
添加-PSSnapin PSEventing -ErrorAction SilentlyContinue
$queryString = @' SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.Name = 'w3svc' AND TargetInstance.State = 'Stopped' '@
$query = 新对象 System.Management.WQLEventQuery `-argumentList $queryString
$watcher = 新对象 System.Management.ManagementEventWatcher($query)
Connect-EventListener 观察者 EventArrived
$watcher.Start()
echo "等待 W3CSVC 服务停止..." Get-Event -wait |
foreach {
Write-Host -foreground Red "W3SVC 服务已停止!" }$watcher.Stop()
Disconnect-EventListener 观察者 EventArrived
回声“完成”
如果我在脚本退出时不执行Disconnect-EventListener位,我会在第三次或第四次附加到事件时获得 CPU 峰值。我的猜测是系统仍在尝试传递事件。
如果您只查找进程的 PID/名称,则可能希望使用 WQL 查询(如“SELECT * FROM Win32_ProcessTrace WHERE TargetInstance.ProcessName = 'name'”(如果适用)* )来获取 Win32_ProcessTrace 事件。
使用“SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32Process' AND TargetInstance.Name = 'name'”的缺陷在于它在后端的工作方式。如果您在 %windir%\system32\wbem\logs 目录中检查 wbemess.log,您会注意到以下日志(使用 __InstanceDeletionEvent):
(2009 年 7 月 22 日星期三 13:58:31 2009.73889577):在命名空间 //./root/CIMV2 中使用查询 select * from __InstanceDeletionEvent 注册通知接收器 10 where TargetInstance ISA 'Win32_Process'。 (2009 年 7 月 22 日星期三 13:58:31 2009.73889577):在命名空间 //./root/CIMV2 中使用查询 select * from __InstanceDeletionEvent 激活过滤器 047209E0,其中 TargetInstance ISA 'Win32_Process' 在 10 内。 (2009 年 7 月 22 日星期三 13:58:31 2009.73889577):使用查询 select * from __ClassOperationEvent 激活过滤器 0225E560,其中 TargetClass 是命名空间 //./root/CIMV2 中的“Win32_Process”。 (2009 年 7 月 22 日星期三 13:58:31 2009.73889577):使用提供程序 $Core 激活过滤器'select * from __ClassOperationEvent where TargetClass isa "Win32_Process"' (2009 年 7 月 22 日星期三 13:58:31 2009.73889587):使用提供程序 $Core 激活过滤器 'select * from __InstanceDeletionEvent 10 where TargetInstance ISA 'Win32_Process'' (2009 年 7 月 22 日星期三 13:58:31 2009.73889587): 发起轮询查询 select * from Win32_Process 以满足事件查询 select * from __InstanceDeletionEvent 10 where TargetInstance ISA 'Win32_Process' (Wed Jul 22 13:58:31 2009.73889587):在命名空间“//./root/CIMV2”中执行轮询查询“select * from Win32_Process” (2009 年 7 月 22 日星期三 13:58:31 2009.73889697):轮询查询 'select * from Win32_Process' 完成 (Wed Jul 22 13:58:41 2009.73899702):在命名空间“//./root/CIMV2”中执行轮询查询“select * from Win32_Process” (2009 年 7 月 22 日星期三 13:58:41 2009.73899792):轮询查询 'select * from Win32_Process' 完成
如您所见,远程计算机上的实际事件实现是按您在 WITHIN 子句中的值指定的时间间隔对 Win32_Process 执行查询。因此,在该轮询中启动和停止的任何进程都不会触发事件。
您可以将 WITHIN 子句设置为一个较小的值来尝试最小化这种影响,但更好的解决方案是使用像 Win32_ProcessTrace 这样的真实事件,它应该始终触发。
*请注意,MSDN 指出 Win32_ProcessTrace 至少需要客户端计算机上的 Windows XP 和服务器计算机上的 Windows 2003 才能工作。如果您使用的是较旧的操作系统,则可能无法使用 __InstanceModificationEvent 查询。
这不完全是你在现实世界中的做法,但应该有所帮助。这似乎根本无法驱动我的 CPU。
static void Main(string[] args)
{
// Getting all instances of notepad
// (this is only done once here so start up some notepad instances first)
// you may want use GetProcessByPid or GetProcesses and filter them as required
Process[] processesToWatch = Process.GetProcessesByName("notepad");
foreach (var process in processesToWatch)
{
process.EnableRaisingEvents = true;
process.Exited +=
(s, e) => Console.WriteLine("An instance of notepad exited");
}
Thread watchThread = new Thread(() =>
{
while (true)
{
Process[] processes = Process.GetProcesses();
foreach (var process in processes)
{
Console.WriteLine("{0}:{1}", process.Id, process.ProcessName);
}
// Don't dedicate a thread to this like I'm doing here
// setup a timer or something similiar
Thread.Sleep(2000);
}
});
watchThread.IsBackground = true;
watchThread.Start();
Console.WriteLine("Polling processes and waiting for notepad process exit events");
Console.ReadLine();
}
My answer here mentions an alternative other than WMI:https://stackoverflow.com/a/50315772/3721646 WMI queries can cost heavy CPU performance if not designed properly. If an intrinsic event from Win32_Process class is used to track process creation event, this impacts performance heavily. An alternate approach is to leverage Security Audit logs. You can enable Process Tracking using Local Security Policy or using a GPO in case of multiple machines. Once the process tracking starts, you can subscribe to security event logs with a custom XML query to monitor certain processes of your interest. The process creation event ID is 4688. `
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[EventData[Data[@Name='NewProcessName'] ='C:\Windows\explorer.exe']]
and
*[System[(EventID=4688)]]
</Select>
</Query>
</QueryList>