tech.guitarrapc.cóm

Technical updates

PowerShell で プロセスの優先度(priority) を変更する

小ネタです。

ある場面において、プロセスの優先順位*1 を変更する機会があるかと思います。

そんな時、良く紹介されているのは、 タスクマネージャーからの優先度の変更ですね。

f:id:guitarrapc_tech:20140212051857p:plain

しかし、長時間のプロセス、かつ処理ごとにプロセスが立ち上がり直す場合には、やってられません。

ということで、PowerShell で変更する方法を見てみましょう。

目次

プロセスの取得

まずは、プロセスを取得してみましょう。

Get-Process*2で見てみましょう。

Get-Process -Name powershell | select *

どばーってでましたが、大事なのはPriorityClassです。

PriorityClass              : Normal

全体

__NounName                 : Process
Name                       : powershell
Handles                    : 441
VM                         : 649650176
WS                         : 81375232
PM                         : 72654848
NPM                        : 26200
Path                       : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Company                    : Microsoft Corporation
CPU                        : 1.96875
FileVersion                : 6.3.9600.16384 (winblue_rtm.130821-1623)
ProductVersion             : 6.3.9600.16384
Description                : Windows PowerShell
Product                    : MicrosoftR WindowsR Operating System
Id                         : 10412
PriorityClass              : Normal
HandleCount                : 441
WorkingSet                 : 81375232
PagedMemorySize            : 72654848
PrivateMemorySize          : 72654848
VirtualMemorySize          : 649650176
TotalProcessorTime         : 00:00:01.9687500
BasePriority               : 8
ExitCode                   : 
HasExited                  : False
ExitTime                   : 
Handle                     : 4488
MachineName                : .
MainWindowHandle           : 525428
MainWindowTitle            : Windows PowerShell
MainModule                 : System.Diagnostics.ProcessModule (powershell.exe)
MaxWorkingSet              : 1413120
MinWorkingSet              : 204800
Modules                    : {System.Diagnostics.ProcessModule (powershell.exe), System.Diagnostics.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule (K
                             ERNEL32.DLL), System.Diagnostics.ProcessModule (KERNELBASE.dll)...}
NonpagedSystemMemorySize   : 26200
NonpagedSystemMemorySize64 : 26200
PagedMemorySize64          : 72654848
PagedSystemMemorySize      : 449832
PagedSystemMemorySize64    : 449832
PeakPagedMemorySize        : 86937600
PeakPagedMemorySize64      : 86937600
PeakWorkingSet             : 94642176
PeakWorkingSet64           : 94642176
PeakVirtualMemorySize      : 669585408
PeakVirtualMemorySize64    : 669585408
PriorityBoostEnabled       : True
PrivateMemorySize64        : 72654848
PrivilegedProcessorTime    : 00:00:00.4218750
ProcessName                : powershell
ProcessorAffinity          : 255
Responding                 : True
SessionId                  : 1
StartInfo                  : System.Diagnostics.ProcessStartInfo
StartTime                  : 2014/02/12 4:37:15
SynchronizingObject        : 
Threads                    : {7776, 9344, 1704, 13264...}
UserProcessorTime          : 00:00:01.5468750
VirtualMemorySize64        : 649650176
EnableRaisingEvents        : False
StandardInput              : 
StandardOutput             : 
StandardError              : 
WorkingSet64               : 81375232
Site                       : 
Container                  : 

PriorityClass を設定する

単純に、タスクマネージャーで表示される優先度をセットすればokです。

$ps = Get-Process -Name powershell
$ps.PriorityClass = "High"

これで、優先度は変更されます。

f:id:guitarrapc_tech:20140212052741p:plain

さて、もう少し良く見てみましょう。

Get-Process -Name powershell | select PriorityClass | Get-Member

ということで、 NoteProperty ですね。

   TypeName: Selected.System.Diagnostics.Process

Name          MemberType   Definition                                                  
----          ----------   ----------                                                  
Equals        Method       bool Equals(System.Object obj)                              
GetHashCode   Method       int GetHashCode()                                           
GetType       Method       type GetType()                                              
ToString      Method       string ToString()                                           
PriorityClass NoteProperty System.Diagnostics.ProcessPriorityClass PriorityClass=Normal

ご存知の通り、[System.Diagnostics.ProcessPriorityClass] はEnumです。

[System.Diagnostics.ProcessPriorityClass]
IsPublic IsSerial Name                                     BaseType                                                                                               
-------- -------- ----                                     --------                                                                                               
True     True     ProcessPriorityClass                     System.Enum                                                                                            

で、設定可能な定義が入っていると。

[Enum]::GetNames([System.Diagnostics.ProcessPriorityClass])
Normal
Idle
High
RealTime
BelowNormal
AboveNormal

ここまでわかっていれば、さくっとファンクションにします。

コード

GitHub においておきます。

全文です。

function Set-ProcessPriorityClass
{
    [CmdletBinding()]
    param
    (
        [parameter(
            mandatory,
            position = 0,
            valueFromPipeline,
            valueFromPipelineByPropertyName)]
        [string[]]
        $Name,

        [parameter(
            mandatory = 0,
            position = 1,
            valueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [System.Diagnostics.ProcessPriorityClass]
        $Priority,

        [parameter(
            mandatory = 0,
            position = 2)]
        [switch]
        $WhatIf
    )

    begin
    {
        DATA message
        {
            ConvertFrom-StringData `
            "
                PriorityNotChange  = Process Name '{0}' Priority '{1}' was already same as Priority '{2}' you set. Skip priority change.
                PriorityChange     = Process Name '{0}' changed Priority from '{1}' to '{2}'.
                ProcessNotFound    = Process Name '{0}' not found. Skip priority change.
            "
        }
    }

    process
    {
        foreach ($n in $Name)
        {
            try
            {
                # Get process
                $ps = Get-Process | where Name -eq $n

                # process exist check
                if ($null -ne $ps)
                {
                    # what if check
                    if ($PSBoundParameters.WhatIf.IsPresent)
                    {
                        $Host.UI.WriteLine(("What if: " + $message.PriorityChange -f $ps.Name, $ps.PriorityClass, $Priority))
                    }
                    else
                    {
                        # process priority check
                        if ($ps.PriorityClass -ne $Priority)
                        {
                            # execute
                            Write-Verbose ($message.PriorityChange -f $ps.Name, $ps.PriorityClass, $Priority)
                            $ps.PriorityClass = $Priority
                        }
                        else
                        {
                            # priority want to change was same as current.
                            Write-Verbose ($message.PriorityNotChange -f $ps.Name, $ps.PriorityClass, $Priority)
                        }
                    }
                }
                else
                {
                    # process missing
                    Write-Warning ($message.ProcessNotFound -f $n)
                }
            }
            finally
            {
                # dispose item
                if ($ps -ne $null){$ps.Dispose()}
            }
        }
    }
}

利用方法

いわゆる PowerShellファンクションとして利用可能です。 Name には、複数のプロセスをまとめて指定可能です。

パラメータ指定

パラメータを指定してもいいですね。 もちろん Priority は、インテリセンスに候補もでます。

Set-ProcessPriorityClass -Name "powershell" -Priority High -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'Normal' to 'High'.
パイプライン渡し

パイプラインからも渡せます。

Get-Process | where Name -eq PowerShell | Set-ProcessPriorityClass -Priority Normal -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'High' to 'Normal'.
スプラッティング渡し

いわゆる Splatting = HashTable に定義して渡す手法も可能です。

$param = @{
    Name = "powershell"
    Priority = "High"}
Set-ProcessPriorityClass @param -Verbose
VERBOSE: Process Name 'powershell' changed Priority from 'Normal' to 'High'.
WhatIf

-WhatIf スイッチを付けることで、実行しないでどうなるかも事前に確認可能です。*3

Get-Process | where Name -eq PowerShell | Set-ProcessPriorityClass -Priority Normal -Verbose -WhatIf
What if: Process Name 'powershell' changed Priority from 'High' to 'Normal'.

まとめ

あとは、PSEvent でも while でもお好きなようにイベントを処理してください。

ふだん使うことは、まずないですね。

*1:いわゆる Priority

*2:Alias は ps です。

*3:実行せずに確認のみです