Processzor terhelés legyűjtése PowerShell segítségével

Az egyik legutóbbi Management Pack fejlesztési munka során belefutottam abba problémába, hogy se PowerShell, se WMI segítségével nem lehet lekérdezni, hogy az éppen futó folyamatok hány százalékát használják a rendszerben elérheto processzoroknak. Az Operations Manager képes PowerShell alapú workflow-kat is használni, így a monitorozás megoldások is hatékonyabbak lesznek, hiszen képesek vagyunk a PowerShell motor minden képességét kihasználni.

A feladat lényege az, hogy ütemezetten le kell kérdezni, hogy egy terminál szerveren az egyes felhasználók mekkora processzor eroforrást használnak fel.

Ebben az esetben két (+1) problémát kell kezelnünk:

1.       Folyamatok társítása a futtató felhasználókhoz

Alapértelmezetten a get-process cmdlet nem támogatja ezen tulajdonság elérését, ezért a WMI objektumokhoz kell fordulnunk. A keresett osztály a WIN32_Process, mivel a visszadott objektumok rendelkeznek egy GetOwner() metódussal.

((gwmi 'win32_process' | ? {$_.name -eq 'outlook.exe'}).getowner()).user

 

A fenti parancs visszatérési értéke az outlook.exe folyamatot futtató felhasználó.

2.       A CPU load meghatározása egyes folyamatok esetében

Mint a poszt elején már említettem, a Windows alaphelyzetben nem tárolja le az aktuálisan futó folyamatok processzor terhelését, ezt maguknak kell kiszámolni.

Ehhez szintén a WMI-hoz kell nyúlnunk, azon belül is a Win32_PerfRawData_PerfProc_Process osztályhoz. Ez az osztály tárol információt a folyamatok teljesítmény adatairól. Az aktuális terhelés meghatározásához két tulajdonságot kell figyelembe vennünk.

-           PercentProcessorTime

Ez a tulajdonság tárolja azt az idotartamot (tick-ben), mikor a folyamat használta processzor eroforrásait

-          Timestamp_Sys100NS

A mintavételezés pontos idopontja (tick-ben)

A számoláshoz két mintavételt kell végeznünk, majd a 2 objektumon elvégezni a muveletet. A $Minta1 változó tárolja az elso mintavételt, a $Minta2 tárolja a második mintavételt

($Minta2.PercentProcessorTime - $Minta1.PercentProcessorTime)/($Minta2. Timestamp_Sys100NS - $Minta1. Timestamp_Sys100NS)

Elso látásra ez jónak is tunhet, csak egy probléma van. A PercentProcessorTime tulajdonságban található érték az összes processzorra vetítve mutat értéket, így ez nem pontos. A kapott értéket még el kell osztanunk a rendszerben található processzorok számával. Kihasználva a környezeti változók könnyu elérhetoségét PowerShellben ezt egyszeruen megtehetjük:

( ($Minta2.PercentProcessorTime - $Minta1.PercentProcessorTime)/($Minta2. Timestamp_Sys100NS - $Minta1. Timestamp_Sys100NS) ) / $env: NUMBER_OF_PROCESSORS

2+1. Rendezés

Ezzel a két legfontosabb problémát megoldottuk, már csak egy van hátra az pedig a kapott adatok rendezése. Mivel a PowerShell az esetek 90%-ban objektumokkal és tömbökkel dolgozik, mi is szeretjük az értékeket így megjeleníteni. Hiszen mennyivel egyszerubb a végeredményt tovább „csövezni” egy Group-Object, vagy Sort-Object cmdlet-be…

Ehhez két dologra van szükségünk. Eloször létrehozunk egy üres tömböt, minden jellegu tipusdeklaráció nélkül:

$CustomArray = @()

Illetve a cikluson belül létrehozunk egy Custom objektumot a megfelelo tulajdonságokkal, majd hozzáadjuk a tömbünkhöz:

 

 foreach($Process in $CurrentProcesses)
 
 {
 
 $result = New-Object System.Object
 
 $result | Add-Member -Type noteproperty -Name Name -Value $Process.Name
 
 $result | Add-Member -Type noteproperty -Name ProcessID -Value $Process.ProcessId
 
 $customarray += $result
 
 }
 

 

 A végleges script körülbelül így fog kinézni:

   

 $CurrentProcesses = gwmi win32_process
 
 $PerfSnapshot_1 = gwmi Win32_PerfRawData_PerfProc_Process
 
 
 
 start-sleep -Seconds 1
 
 
 
 $PerfSnapshot_2 = gwmi Win32_PerfRawData_PerfProc_Process
 
 
 
 $CustomArray = @()
 
 
 
 foreach($Process in $CurrentProcesses)
 
 {
 
 [double]$CPULoad = 0
 
 $result = New-Object System.Object
 
 
 
 $result | Add-Member -Type noteproperty -Name Name -Value $Process.Name
 
 
 
 $result | Add-Member -Type noteproperty -Name ProcessID -Value $Process.ProcessId
 
 
 
 $CurrentOwner = $Process.GetOwner()
 
 $result | Add-Member -Type noteproperty -Name UserName -Value ($CurrentOwner.Domain + '\' + $CurrentOwner.User)
 
 
 
 $CPULoad = (((($PerfSnapshot_2 | ? {$_.idprocess -eq $Process.ProcessId}).PercentProcessorTime - ($PerfSnapshot_1 | ? {$_.idprocess -eq $Process.ProcessId}).PercentProcessorTime) / (($PerfSnapshot_2 | ? {$_.idprocess -eq $Process.ProcessId}).Timestamp_Sys100NS - ($PerfSnapshot_1 | ? {$_.idprocess -eq $Process.ProcessId}).Timestamp_Sys100NS)) * 100) / ($env:NUMBER_OF_PROCESSORS)
 
 $result | Add-Member -Type noteproperty -Name CPU_Util -Value $CPULoad
 
 
 
 $customarray += $result
 
 }
 

 

A végeredmény - ami a $CustomArray változóban került letárolásra -, tartalmazza a folyamatok nevét, azonosítóját, a futtató felhasználó nevét, illetve a mért idoszakban okozott processzor terhelést.

Az eredmény nálam ez lett:

 

Mivel egy tömbben tároltuk le a script kimenetét, a végeredményt képesek vagyunk manipulálni a beépített cmdlet segítségével. Például, ha arra vagyunk kíváncsiak, hogy egyes felhasználók hány folyamatot futtatnak éppen:

                $customarray | group -Property UserName | ft –AutoSize

Aminek az eredménye:

 

Így már a processzor terhelés összeadása felhasználónként csak kézügyesség kérdése…