Processo WMI Assistindo usa muita CPU! Qualquer método melhor?
Pergunta
Eu preciso assistir quando certos processos são iniciados ou parados em uma máquina Windows. Atualmente estou aproveitado para o sistema WMI e consultando-lo a cada 5 segundos, mas isso faz com que a CPU pico a cada 5 segundos, porque WMI é WMI. Existe um jeito melhor de fazer isso? Eu poderia apenas fazer uma lista de processos em execução e anexar um evento Exited a eles através do Namespace System.Diagnostics, mas não há nenhum manipulador de eventos para a criação.
Solução
Este não é exatamente como você faria no mundo real, mas deve ajudar. Isto parece não dirigir meu CPU muito em tudo.
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();
}
Outras dicas
Eu tive picos de CPU quando ouvir eventos WMI nos casos em que não conseguiram separar adequadamente dos meus eventos de saída / limpeza. Você pode querer verificar que você não está "vazando" inscrições de evento WMI. Apenas no caso de desconexão do evento o mais cedo possível e certifique-se que você sempre fazê-lo.
Para ilustrar ainda mais, aqui está um exemplo de meu livro PowerShell que escutas para eventos WMI usando a biblioteca PSEventing:
Add-PSSnapin PSEventing -ErrorAction SilentlyContinue
$ queryString = @' SELECT * DE __InstanceModificationEvent Dentro de 10 ONDE TargetInstance ISA 'Win32_Service' E TargetInstance.Name = 'w3svc' E TargetInstance.State = 'Parado' '@
$ query = New-Object System.Management.WQLEventQuery ` -ArgumentList $ queryString
$ observador = New-Object System.Management.ManagementEventWatcher ($ query)
Connect-EventListener observador EventArrived
$ watcher.Start ()
echo "Esperando o serviço W3CSVC a parada ..." Get-Event -wait |
foreach {
Write-Host -foreground Red "O serviço W3SVC parou!" }$ watcher.Stop ()
Desconectar-EventListener observador EventArrived
echo "feito"
Se eu não fizer o Desconectar-EventListener pouco após a saída de script, recebo picos de CPU a terceira ou quarta vez que atribuo ao evento. Meu palpite é que o sistema ainda tenta entregar eventos.
Se você está apenas procurando PID / Nome de seus processos, você pode em vez disso quiser pegar em eventos Win32_ProcessTrace, usando uma consulta WQL como "SELECT * FROM Win32_ProcessTrace ONDE TargetInstance.ProcessName = 'nome'" se aplicável * .
A armadilha de usar "SELECT * FROM __InstanceModificationEvent dentro de 10 ONDE TargetInstance ISA 'Win32Process' E TargetInstance.Name = 'nome'" está na forma como ele funciona no back-end. Se você inspecionar wbemess.log dentro de sua% windir% \ system32 \ wbem \ logs, você vai notar os seguintes logs (usando __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
Como você pode ver, a implementação evento real na máquina remota é a realização de uma consulta em relação Win32_Process em um intervalo especificado pelo seu valor na cláusula WITHIN. Como resultado, todos os processos que iniciar e parar dentro dessa pesquisa nunca vai acionar um evento.
Você pode definir a cláusula WITHIN para um valor pequeno para tentar minimizar este efeito, mas a melhor solução é usar um verdadeiro evento como Win32_ProcessTrace, que deve sempre fogo.
* Nota que MSDN indica Win32_ProcessTrace requer um mínimo de Windows XP em uma máquina cliente e Windows 2003 em uma máquina servidor ao trabalho. Se você estiver trabalhando com um sistema operacional mais antigo, você pode ser preso usando a consulta __InstanceModificationEvent.
A minha resposta aqui menciona uma outra alternativa senão WMI: https://stackoverflow.com/a/50315772/3721646 consultas WMI pode custar o desempenho da CPU pesado se não projetada corretamente. Se um evento intrínseco da classe Win32_Process é usado para rastrear evento de criação processo, este afeta o desempenho fortemente. Uma abordagem alternativa é aproveitar os logs de auditoria de segurança. Você pode ativar o controle de processos utilizando a política de segurança local ou usando um GPO em caso de várias máquinas. Uma vez que o rastreamento processo começa, você pode se inscrever para logs de eventos de segurança com uma consulta XML personalizado para monitorar determinados processos de seu interesse. O evento ID criação processo é 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>