WMI Process Watching uses too much CPU! Any better method?

萝らか妹 提交于 2019-12-19 04:14:33

问题


I need to watch when certain processes are started or stopped on a Windows machine. I'm currently tapped into the WMI system and querying it every 5 seconds, but this causes a CPU spike every 5 seconds because WMI is WMI. Is there a better way of doing this? I could just make a list of running processes and attach an Exited event to them through the System.Diagnostics Namespace, but there is no Event Handler for creation.


回答1:


This is not exactly how you'd do it in the real world but should help. This seems not to drive my CPU much at all.

    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();
    }



回答2:


I've had CPU spikes when listening to WMI events in cases where I have failed to detach properly from my events on exit/cleanup. You might want to check you are not "leaking" WMI event subscriptions. Just in case detach from the event as early as possible and make sure you always do it.

To illustrate further, here's an example from my PowerShell book that listens to WMI events using the PSEventing library:

Add-PSSnapin PSEventing -ErrorAction SilentlyContinue

$queryString = @' SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.Name = 'w3svc' AND TargetInstance.State = 'Stopped' '@

$query = New-Object System.Management.WQLEventQuery ` -argumentList $queryString

$watcher = New-Object System.Management.ManagementEventWatcher($query)

Connect-EventListener watcher EventArrived

$watcher.Start()

echo "Waiting for the W3CSVC service to stop..." Get-Event -wait | foreach { Write-Host -foreground Red "The W3SVC service has stopped!" }

$watcher.Stop()

Disconnect-EventListener watcher EventArrived

echo "done"

If I do not do the Disconnect-EventListener bit upon script exit, I get CPU spikes the third or fourth time I attach to the event. My guess is that the system still tries to deliver events.




回答3:


If you are only looking for PID/Name of your processes, you may instead wish to pick up on Win32_ProcessTrace events, using a WQL query such as "SELECT * FROM Win32_ProcessTrace WHERE TargetInstance.ProcessName = 'name'" if applicable*.

The pitfall of using "SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32Process' AND TargetInstance.Name = 'name'" is in how it works on the back end. If you inspect wbemess.log within your %windir%\system32\wbem\logs directory, you will notice the following logs (using __InstanceDeletionEvent):

(Wed Jul 22 13:58:31 2009.73889577) : Registering notification sink with query select * from __InstanceDeletionEvent within 10 where TargetInstance ISA 'Win32_Process' in namespace //./root/CIMV2.
(Wed Jul 22 13:58:31 2009.73889577) : Activating filter 047209E0 with query select * from __InstanceDeletionEvent within 10 where TargetInstance ISA 'Win32_Process' in namespace //./root/CIMV2.
(Wed Jul 22 13:58:31 2009.73889577) : Activating filter 0225E560 with query select * from __ClassOperationEvent where TargetClass isa "Win32_Process" in namespace //./root/CIMV2.
(Wed Jul 22 13:58:31 2009.73889577) : Activating filter 'select * from __ClassOperationEvent where TargetClass isa "Win32_Process"' with provider $Core
(Wed Jul 22 13:58:31 2009.73889587) : Activating filter 'select * from __InstanceDeletionEvent within 10 where TargetInstance ISA 'Win32_Process'' with provider $Core
(Wed Jul 22 13:58:31 2009.73889587) : Instituting polling query select * from Win32_Process to satisfy event query select * from __InstanceDeletionEvent within 10 where TargetInstance ISA 'Win32_Process'
(Wed Jul 22 13:58:31 2009.73889587) : Executing polling query 'select * from Win32_Process' in namespace '//./root/CIMV2'
(Wed Jul 22 13:58:31 2009.73889697) : Polling query 'select * from Win32_Process' done
(Wed Jul 22 13:58:41 2009.73899702) : Executing polling query 'select * from Win32_Process' in namespace '//./root/CIMV2'
(Wed Jul 22 13:58:41 2009.73899792) : Polling query 'select * from Win32_Process' done

As you can see, the actual event implementation on the remote machine is to perform a query against Win32_Process on an interval that is specified by your value in the WITHIN clause. As a result, any processes that start and stop within that poll will never fire an event.

You can set the WITHIN clause to a small value to try and minimize this effect, but the better solution is to use a true event like Win32_ProcessTrace, which should always fire.

*Note that MSDN indicates Win32_ProcessTrace requires a minimum of Windows XP on a client machine and Windows 2003 on a server machine to work. If you are working with an older OS, you may be stuck using the __InstanceModificationEvent query.




回答4:


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>


来源:https://stackoverflow.com/questions/345199/wmi-process-watching-uses-too-much-cpu-any-better-method

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!