关于powershell:如果进程达到CPU使用率的x%,则杀死该进程

Kill a process if it reaches x% of CPU usage

我想停止正在运行的CPU使用率超过14%的进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$process = get-process
    foreach ($proc in (Get-WmiObject  Win32_Processor)){
if($proc.numberofcores -eq $null){
    $cores++
}else{
    $cores = $cores + $proc.numberofcores
}
}
foreach($name in $process){
    $processName = $name.processName
foreach($hog in $processName){
       $cpuusage = [Math]::round(((((Get-Counter"\\Process($processName)\\%
Processor Time"
-MaxSamples 2).Countersamples)[0].CookedValue)/$cores),2)

        if($cpuusage -gt 14){
            Stop-Process -Name $processName
        }
}
}

我收到以下错误消息,仅此而已。我希望Idle(0)无法正常工作,但其他任何操作都不会被杀死。

Stop-Process : Cannot stop process"Idle (0)" because of the following
error: Access is denied
At line:14 char:17
+ Stop-Process -Name $processName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (System.Diagnostics.Process
(Idle):Process) [Stop-Process], ProcessCommandException
+ FullyQualifiedErrorId :
CouldNotStopProcess,Microsoft.PowerShell.Commands.StopProcessCommand

我试图在第二个foreach循环中将$processName变量替换为$hog,但仍然出现相同的错误。

在阅读@JosefZ答案后,我得到了满足我课程要求的内容。在此处发布以供参考;

1
2
3
4
5
6
7
8
9
10
11
12
$process = get-process
foreach ($pro in $process){
    $name = $pro.ProcessName
    $CpuCores = (Get-WmiObject -Class Win32_Processor).NumberOfCores
    $CpuValue = ((Get-Counter"\\Process($name)\\% Processor Time").CounterSamples.CookedValue)/$CpuCores
    $percent = [Decimal]::Round($CpuValue, 3)
        if($percent -ge 15){
        Stop-Process -Name $name
        $wshell = New-Object -ComObject Wscript.Shell
        $wshell.Popup("Process $name was using more than $percent % CPU. We have eliminated it.",0,"Ok",0x1)
    }
}

请注意,性能计数器通常受访问控制列表(ACL)的保护。要获取所有可用的性能计数器,请使用"以管理员身份运行"选项打开Windows PowerShell,并且您停止进程的能力取决于您的权限。

以下脚本用于耗时CPU wmic path cim_datafile和防病毒完全扫描的调试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Set-StrictMode -Version latest
$process = get-process
$cores = 0
foreach ($proc in (Get-WmiObject  Win32_Processor)){
    if($proc.numberofcores -eq $null){
        $cores++
    }else{
        $cores = $cores + $proc.numberofcores
    }
}
### $cores
foreach($name in $process){
    $processName = $name.processName
    if ( $processName -notmatch"^Idle" ) {
        foreach($hog in $processName){
            $cpuusage = -1
            $cpuusage = [Math]::round(((((Get-Counter"\\Process($processName)\\% Processor Time" -MaxSamples 2).Countersamples)[0].CookedValue)/$cores),2)
            if ($cpuusage -gt 14) {
                Stop-Process -Name $processName -PassThru -ErrorAction Continue
                ###"{0} {1} {2} {3}" -f '+', $cpuusage, $name.Id, $processName
            } else {
                if($cpuusage -ne 0){
                    ###"{0} {1} {2} {3}" -f '-', $cpuusage, $name.Id, $processName
                }
            }
        }
    }
}

脏溶液:Stop-Process -Name $processName -ErrorAction SilentlyContinue -PassThru

"空闲"表示"无效"(未运行或未使用)。当"系统空闲进程"位于100 %时,表示没有任何资源在使用CPU资源。

在MSDN上读取停止进程(Stop-Process):

Windows PowerShell gives you flexibility for listing processes, but
what about stopping a process?

The Stop-Process cmdlet takes a Name or Id to specify a process
you want to stop. Your ability to stop processes depends on your
permissions. Some processes cannot be stopped. For example, if you
try to stop the idle process, you get an error:

1
2
3
4
5
PS> Stop-Process -Name Idle
Stop-Process : Process 'Idle (0)' cannot be stopped due to the following error:
 Access is denied
At line:1 char:13
+ Stop-Process  <<<< -Name Idle

阅读Get-Help 'Stop-Process' -Online

Outputs

None, System.Diagnostics.Process

This cmdlet returns a System.Diagnostics.Process object that represents the stopped process, if you specify the PassThru
parameter. Otherwise, this cmdlet does not generate any output.


为您提供其他指针的夫妇。首先,您应该使用Win32_ComputerSystemNumberOfLogicalProcessors代替Win32_ProcessorNumberOfCores。原因是,性能计数器会在拥有该功能的系统上考虑到超线程,因此基于物理内核进行的计算将为您提供处理器时间值,该值是其应有大小的两倍。例如,在我的具有6个物理核心的机器上,由于超线程,我有12个逻辑处理器。使用您的原始计算,使用8%CPU的进程将被报告为16%,因此将被错误地停止。 Win32_ComputerSystem版本将返回所有物理CPU上的所有逻辑处理器,因此也可以正确计算多插槽服务器。

1
2
3
4
C:\\WINDOWS\\system32> (Get-WmiObject win32_processor).NumberofCores
6
C:\\WINDOWS\\system32> (Get-WmiObject win32_computersystem).Numberoflogicalprocessors
12

第二,您应该使用ID而不是名称来停止该进程,因为这将导致意想不到的后果。一个简单的示例是Chrome,每个选项卡都有一个进程,所有这些进程的名称都为Chrome。因此,单个选项卡可能会出现导致CPU占用率较高的问题,但是您调用Stop-Process -Name Chrome的脚本会关闭所有Chrome实例,包括那些没有执行任何操作的实例。

以下示例脚本解决了这两个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Get all cores, which includes virtual cores from hyperthreading
$cores = (Get-WmiObject  Win32_ComputerSystem).NumberOfLogicalProcessors
#Get all process with there ID's, excluding processes you can't stop.
$processes = ((Get-Counter"\\Process(*)\\ID Process").CounterSamples).where({$_.InstanceName -notin"idle","_total","system"})
#Get cpu time for all processes
$cputime = $processes.Path.Replace("id process","% Processor Time") | get-counter | select -ExpandProperty CounterSamples
#Get the processes with above 14% utilisation.
$highUsage = $cputime.where({[Math]::round($_.CookedValue / $cores,2) -gt 14})
# For each high usage process, grab it's process ID from the processes list, by matching on the relevant part of the path
$highUsage |%{
    $path = $_.Path
    $id = $processes.where({$_.Path -like"*$($path.Split('(')[1].Split(')')[0])*"}) | select -ExpandProperty CookedValue
    Stop-Process -Id $id -Force -ErrorAction SilentlyContinue
}

请注意,使用.where(语法需要PowerShell 5或更高版本。该脚本还具有比在foreach循环中调用Get-Counter更快的优点。