かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  vol.200 タスクマネージャーが示すCPU使用率の秘密|セイテク・シス管道場(Web)

 

 

vol.200 タスクマネージャーが示すCPU使用率の秘密|セイテク・シス管道場(Web)

2026年05月11日配信
執筆者:山内 和朗

 「セイテク・シス管道場(Web)」では、Windows Serverの要素技術やシステム管理の基本的な部分に焦点を当てたいと思います。前回に引き続き、みんなの強い味方「タスクマネージャー」、それも「パフォーマンス」タブの「CPU」について深掘りします。「パフォーマンス」タブの「CPU」では、CPU使用率のグラフ以外にCPUに関係する各種情報を入手できます。で確認できる使用率のグラフ以外の情報について見ていきましょう。その情報をタスクマネージャー以外の方法(PowerShell)で取得する方法についても紹介します。

 

プロセッサ名


 「CPU」の上部に表示されるCPUの名前です。説明はいらないでしょう(画面1)。

 

 (get-CimInstance Win32_Processor).Name  

 

画面1 タスクマネージャーの「パフォーマンス」タブの「CPU」に表示される各種情報をPowerShellで取得してみる
画面1 タスクマネージャーの「パフォーマンス」タブの「CPU」に表示される各種情報をPowerShellで取得してみる

基本速度

 

 続いて、グラフ下の右側から。「基本速度」は、CPUのベースクロック周波数です。次のコードでGHz単位のベースクロック周波数を取得できます。

 

$baseclock = (Get-CimInstance Win32_Processor).MaxClockSpeed
または
$baseclock = (Get-Counter '\Processor Information(_Total)\Processor Frequency').CounterSamples.CookedValue

#Hz表示
$baseclock

#GHz表示
"{0:N2}" -f ($baseclock / 1000)

 

ソケット、コア、論理プロセッサ数(仮想プロセッサ)

 

 「ソケット」は、マザーボードに物理的に取り付けられたCPUの数のことを指し、「コア」はすべてのプロセッサの合計コア数を示します。「論理プロセッサ数」は、ハイパースレッディングが無効な場合コア数と同じになりますが、有効な場合はコア数×2になります。OSが仮想マシン(VM)(Hyper-V VM以外は未確認)で稼働中の場合は、「コア」は表示されず、論理プロセッサ数が「仮想プロセッサ」として表示されます。ただし、入れ子になった仮想化(Nested Virtualization)が有効で、Hyper-Vが実行中の場合、物理マシンと同じように、「ソケット」「コア」「論理プロセッサ数」の表示になります。

 「ソケット」「コア」「論理プロセッサ数(VMの仮想プロセッサ数)」は、次のコマンドラインで取得できます。

 

(Get-CimInstance Win32_ComputerSystem).NumberOfProcessors
(Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfCores -Sum).Sum
(Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
 

 

仮想化、仮想マシン

 

 物理マシンの場合、「仮想化: 有効または無効」が表示されます。VM(Hyper-V VM以外は未確認)の場合、「仮想マシン: はい」と表示されます。ただし、入れ子になった仮想化(Nested Virtualization)が有効で、Hyper-VやWindows Subsystem for Linux(WSL)2、Windows Sandboxが実行中の場合(=vmcomputeサービスが存在する場合)、物理マシンと同じように、物理マシンと同じ表示になります。「仮想化: はい」とは、既にハイパーバイザーが稼働中であるか、稼働中でない場合はHyper-Vの要件(VMMモード拡張機能あり、ファームウェアで仮想化が有効、第2レベルアドレス変換拡張機能あり、データ実行防止(DEP)有効)を満たしているかどうかを示します。

 タスクマネージャーの場合、「仮想化」「仮想マシン」は一目瞭然ですが、PowerShellで取得するとなると、少し複雑です。たぶん、以下のコードで対応できるでしょう。Hyper-Vの要件は「systeminfo.exe」の「Hyper-Vの要件」や「システム情報(msinfo32.exe)」の「システムの概要」の下部の表示でも確認できますが、コマンドラインからは扱いにくい上、パイパーバイザーが検出される場合(Hyper-Vが有効な場合やHyper-V VMの場合など)に要件が表示されないという制約もあります。そのあたりを踏まえてタスクマネージャーの実際の表示と整合性がある情報を出力するようにコードを記述しました。

 

$w32cs = Get-CimInstance Win32_ComputerSystem
$w32os = Get-CimInstance Win32_OperatingSystem
$w32proc = Get-CimInstance Win32_Processor
if ($w32cs.HypervisorPresent) {
#物理マシンまたはVM
  if ((Get-CimInstance Win32_ComputerSystem).Model -match "Virtual|VMware|KVM|VirtualBox|HVM|Compute") {
    if (Get-Service -Name vmcompute -ErrorAction SilentlyContinue) {
      "仮想化: 有効"
    } else {
      "仮想マシン: はい"
    }
  } else {
    Write-Output "仮想化: 有効"
  }
} elseif ($w32os.DataExecutionPrevention_Available `
 -and $w32proc.VirtualizationFirmwareEnabled `
 -and $w32proc.SecondLevelAddressTranslationExtensions `
 -and $w32proc.VMMonitorModeExtensions ) {
#物理マシン
  Write-Output "仮想化: 有効"
} else {
#物理マシン
  Write-Output "仮想化: 無効"
}

 

L1キャッシュ、L2キャッシュ、L3キャッシュ

 

 これらはCPUが備えているキャッシュメモリのことで、CPUとメモリの間に配置される、小容量ですが、超高速な記憶域です。CPUに近い(L1)ほど高速でサイズは小さくなります。L1キャッシュは1つ目のコードで、L2およびL3キャッシュは2つ目のコードで取得できます。

 

Get-CimInstance Win32_CacheMemory |Where {$_.Level -eq 3}|Select InstalledSize
Get-CimInstance Win32_Processor| Select L2CacheSize, L3CacheSize

 

稼働時間

 

 「稼働時間」は、システムがコールドブートした日時からこれまでに経過した時間です。稼働時間は次のコードで取得できます。

 

$lastbootup = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
((Get-Date) - (Get-Date($lastbootup))).ToString("d\:hh\:mm\:ss")

 

 稼働時間は、実際にシステム(CPU)が稼働していた時間ではないことに注意してください。スリープや休止状態であった時間は考慮されません。稼働時間がリセットされるのは、コールドブートまたは再起動のタイミングです。Windows 10/11で高速スタートアップが有効な場合(既定)は、シャットダウンしても通常、リセットされることはありません。*1 最後にコールドブートまたはスリープや休止状態から再開したときからの経過時間、つまり現在の電源オンからの実稼働時間は、次のコードで取得できると思います。このコードでは、コールドブート(再起動を含む)は、Kernel-GeneralのイベントID 12で、スリープや休止状態からの再開はKernel-PowerのイベントID 107(再開イベント、ただし時刻修正前)とその直後のKernel-GeneralのイベントID 1の組み合わせで判断しています。

 *1 高速スタートアップが有効な場合で完全なシャットダウンを実行するには、[Shift]キーを押しながらシャットダウン操作します。 powercfg /aを実行して、「高速スタートアップ」が利用可能になっていれば、高速スタートアップが有効です。

 

$events = Get-WinEvent -LogName System -FilterXPath "*[System[Provider[@Name='Microsoft-Windows-Kernel-General' or @Name='Microsoft-Windows-Kernel-Power'] and (EventID=12 or EventID=107 or EventID=1)]]" | Sort-Object TimeCreated
$lastresume = $null
for ($i = 0; $i -lt $events.Count; $i++) {
  if ($events[$i].Id -eq 107) {
    for ($j = $i+1; $j -lt $events.Count; $j++) {
      if ($events[$j].Id -eq 1) {
        $lastresume = $events[$j].TimeCreated
        break
      }}}}
$lastcoldboot = $null
$lastcoldboot = ($events | Where-Object {$_.Id -eq 12} | Select-Object -Last 1).TimeCreated
$lastboot = @($lastresume, $lastcoldboot) |  Where-Object { $_ } | Sort-Object -Descending | Select-Object -First 1
(((Get-Date) - $lastboot)).ToString("d\:hh\:mm\:ss")

 

プロセス数、スレッド数、ハンドル数

 

 「プロセス数」「スレッド数」「ハンドル数」は、システム上のすべてのプロセスと、そのスレッド、およびハンドルの合計です。

 

#プロセス数

(Get-Process).Count
#スレッド数

(Get-Process | ForEach-Object {$_.Threads.Count } | Measure-Object -Sum).Sum
#ハンドル数
(Get-Process |Measure-Object Handles -Sum).Sum

 

速度

 

 現在のCPUは、使用状況に応じてクロック周波数が動的に変化するのは当たり前あり、システム設定(電源ポリシーなど)に従ってCPU自身が調整します。「速度」は、現在のクロック周波数を示します。そして、このクロック周波数の変化は、次の「使用率」に無関係ではありません。

 

$currentclock = (Get-Counter '\Processor Information(_Total)\Actual Frequency').CounterSamples.CookedValue

#Hz表示

$currentclock 

 

#GHz表示

"{0:N2}" -f ($currentclock  / 1000)

 

使用率

 

 最後に残った「使用率」は最新のCPU使用率を示しています。CPU使用率のグラフ表示の最新値とも言うこともできます。

 Windowsの古いバージョンは、このCPU使用率の精度や一貫性に問題がありました。前回紹介したWindows SysinternalsのProcess Explorerは、独自のロジックにより、その登場当初から精度の高いCPU使用率を提供してきました。タスクマネージャーのCPU使用率の精度は、Windowsのバージョンとともに改善されてきましたが、整数で丸められていることから分かるように、あくまでも近似値です。

 タスクマネージャーのCPU使用率は精度に多少の問題があるものの、パフォーマンスカウンターの「¥Processor(_Total)¥% Processor Time」に相当するものと考えている人は多いと思います。先に言っておくと、タスクマネージャーはパフォーマンスカウンターのAPI(Performance Data Helper《PDH》 API)をしているわけでなく、より低レベルなネイティブAPI(おそらくNtQuerySystemInfomationやEvent Tracing for Windows《ETW》)でCPU使用率をリアルタイムに計測した結果を示しています。

 また、現在のタスクマネージャーのCPU使用率は、「¥Processor(_Total)¥% Processor Time」ではなく、「¥Processor Information(_Total)¥% Processor Utility」に相当するそうです。詳しくは、以下の公式ブログで説明されていますが、「% Processor Time」とは異なり、「% Processor Utility」は使用状況に応じてクロック周波数が動的に変化するCPUに対応しています。また、64を超える論理プロセッサ(プロセッサグループの最大)やNUMA(Non-Uniform Memory Access)にも対応しています。このカウンターは、システムの高性能・高機能化に対応するためにWindows Server 2008 R2で追加されました。なお、「¥Processor Information」にも「% Processor Time」は存在しますが、このカウンターは「¥Processor」の「% Processor Time」と同様にクロック周波数の変化に対応していませんが、64を超える論理プロセッサ、つまりプロセッサグループに対応しています。ちなみに、「パフォーマンスモニター」(perfmon)のグラフに既定で追加されているCPU使用率のカウンターは、Windows 7およびWindows Server 2008 R2のときに、それまでの「¥Processor(_Total)¥% Processor Time から「¥Processor Information(_Total)¥% Processor Time 」に変更されました。

CPU 使用状況を確認する際に見るべきパフォーマンス カウンター|Microsoft Japan Windows Technology Support Blog(Microsoft)

 クロック周波数の動的な変化に対応するため、「% Processor Utility」は100%以上の値を示すことがあります。タスクマネージャーのグラフは100%が上限であるため、タスクマネージャーでは値を0~100%におさまるように調整して表示しています(画面2)。実測した結果に基づいて計算式を考えると、タスクマネージャーのCPU使用率は「% Processor Utility×基本速度/(現在の)速度」で妥当だと思います。この計算で正しいかどうかは保証できませんが、この計算によりシステムの負荷が高まればクロック周波数が増大してCPU使用率が最大100%にスケーリングされ、逆にアイドル時はクロック周波数が抑制されるためCPU使用率が% Processor Utilityの値よりも大きくスケーリングされるはずです。

 

画面2 「% Processor Utility」は100%以上の値を示すことがある。タスクマネージャーはその値を0~100%の範囲に収まるように調整している。その計算式は「% Processor Utility×基本速度/(現在の)速度」が妥当?
画面2 「% Processor Utility」は100%以上の値を示すことがある。タスクマネージャーはその値を0~100%の範囲に収まるように調整している。その計算式は「% Processor Utility×基本速度/(現在の)速度」が妥当だと思う

 この計算式に当てはめるとして、TYPEPERFコマンドとPowerShellでタスクマネージャー相当のCPU使用率を1秒間隔で取得するコードを書いてみました(画面3)。

 

$baseclock = (Get-CimInstance Win32_Processor).MaxClockSpeed
typeperf "\Processor Information(_Total)\% Processor Utility" "\Processor Information(_Total)\Actual Frequency" -si 1 | Select-Object -Skip 2 |
ForEach-Object {
  $p = $_ -split ","
  $utility = [double]($p[1] -replace '"')
  $freq = [double]($p[2] -replace '"')
  $cpu = $utility * $baseclock /$freq
  "CPU 使用率(TYPEPERF): {0:N2} %" -f $cpu
}

 

 以下は、PowerShellのみで実現したコードです。

 

$baseclock = (Get-CimInstance Win32_Processor).MaxClockSpeed
while ($true) {
  $currentclock =
(Get-Counter '\Processor Information(_Total)\Actual Frequency').CounterSamples.CookedValue
  $cpu = (Get-Counter '\Processor Information(_Total)\% Processor Utility').CounterSamples.CookedValue * $baseclock / $currentclock
  "CPU 使用率(GET-COUNTER) {0:N2} %" -f $cpu
 
#Write-Host "`rCPU $($cpu.ToString("N2")) %     " -NoNewline    #常に最新の使用率のみを表示する場合

  Start-Sleep -Seconds 1

 

画面3 TYPEPERFコマンドとPowerShell(左のウィンドウ)とPowerShellのみ(右のウィンドウ)のコードによるCPU使用率の取得
画面3 TYPEPERFコマンドとPowerShell(左のウィンドウ)とPowerShellのみ(右のウィンドウ)のコードによるCPU使用率の取得

 

「% Processor Utility」はメニーコア時代のカウンター

 

 Windowsは、1つのプロセッサグループで最大64の論理プロセッサをサポートしています。「¥Processor」の「% Processor Time」は、Windowsにプロセッサグループの概念が登場する以前から存在していたレガシなカウンターであり、プロセッサグループとは何か知らないため、結果としてプロセッサグループ0のみを認識します。そのため、64論理プロセッサまでは、NUMA対応やソケット数に関係なく、「% Processor Time」で扱える論理プロセッサ数は最大64です(画面4)。

 

vol200_scr04
画面4 「¥Processor」の「% Processor Time」は、最大64論理プロセッサ(CPU 0~63)に対応。これを超える数を認識できない

 64~128論理プロセッサのシステムでは、プロセッサグループが2つに分けられ、それぞれに均等に論理プロセッサが割り当てられます。そのため、例えば72論理プロセッサのシステムの場合、プロセッサグループ0に0~35の論理プロセッサ、プロセッサグループ1に0~35の論理プロセッサが割り当てられます。そのようなシステムでは「¥Processor」の「% Processor Time」はプロセッサグループ0の0~35しか認識せず、_Totalはその36の平均になります。「Processor Information」の「% Processor Time」や「% Processor Utility」は、プロセッサグループ0の0~35とプロセッサグループ1の0~35の論理プロセッサ、プロセッサグループごとの平均(0,_Total、1,_Total)と全体の平均(_Total)を監視できます(画面5)。結果として、64論理プロセッサを超えるシステムにおいて、「¥Processor」の「% Processor Time」は正確なCPU使用率を提供しません(画面6)。

 

画面5 72コアの場合、36コアごとの2つのプロセッサグループに分けられるため、「¥Processor」の「% Processor Time」は35コアの値とその合計しか提供しない。一方、「Processor Information」の「% Processor Time」と「% Processor Utility」はすべてのコアを監視できる
画面5 論理プロセッサ数72のシステムの場合、36個ごとの2つのプロセッサグループに分けられるため、「¥Processor」の「% Processor Time」は35個の論理プロセッサの使用率の値とその合計の平均(_Total)しか提供しない(画面左)。一方、「Processor Information」の「% Processor Time」と「% Processor Utility」はすべての論理プロセッサを監視できる(画面右)

 

画面6 「¥Processor(_Total)¥% Processor Time」は認識可能な35コア(プロセッサグループ0)の平均であるため、62コア全体の正確なCPU使用率を示さない
画面6 「¥Processor(_Total)¥% Processor Time」は72論理プロセッサのうち認識可能な35個(プロセッサグループ0)の平均であるため、全体の正確なCPU使用率を示さない

 より精度の高い監視には、「% Processor Utility」が適しています。100%を超えるかもしれない値をリアルタイムに0~100%に補正する方法については先ほど示しました。しかし、しきい値監視を行うには、上限が最初から100%に固定されている「% Processor Time」で代替して問題はないでしょう。ただし、メニーコア対応のためには「¥Processor」ではなく、「¥Processor Information」のほうの「% Processor Time」を使用してください。論理プロセッサ数64を超えるシステムなんて使う予定がないというなら、従来どおり「¥Processor」の「% Processor Time」 で何の問題もありません。

 

ヒント: 

 VMの実行が中心のHyper-Vサーバーの場合は、「Hyper-V Hypervisor Logical Processor(_Total)¥% Total Run Time」などを監視することも重要です。 このカウンターは、ハイパーバイザーが論理CPUを実行していた時間の割合であり、クロック周波数とは別の概念です。

 仮想化された環境でのボトルネックの検出|Windows Server(Microsoft Learn) 


 さて、タスクマネージャーの「パフォーマンス」タブの「CPU」について深掘りしてきました。他の「メモリ」「ディスク」「イーサネット」「Wi-Fi」「GPU」についても、パフォーマンスデータや情報をタスクマネージャー以外の方法で取得することにチャレンジしてみてください。何かしら、Windowsの理解を深めることができるはずです。例えば、次のPowerShellのコードを実行すると、CPU、メモリ、ディスク(C:)、ネットワーク(総送受信、Kbps)の使用率を表示し、3秒間隔(計算に1秒以上かかる関係で)で表示をリフレッシュさせることができます(画面7)。Server Coreでどうかなぁと思って書いてみましたが、Server Coreでもタスクマネージャー(Taskmgr.exe)は使えること忘れていました。

 

$w32proc = Get-CimInstance Win32_Processor
$baseclock = $w32proc.MaxClockSpeed

while ($true) {
  #CPU
  $currentclock = (Get-Counter '\Processor Information(_Total)\Actual Frequency').CounterSamples.CookedValue
  $cpu = (Get-Counter '\Processor Information(_Total)\% Processor Utility').CounterSamples.CookedValue * $baseclock / $currentclock

  #RAM
  $w32os = Get-CimInstance Win32_OperatingSystem
  $ramtotal = $w32os.TotalVisibleMemorySize
  $ramfree  = $w32os.FreePhysicalMemory
  $ramused = $ramtotal - $ramfree
  $ramusage = ($ramused / $ramtotal) * 100
  
  #DISK(C:)
  $diskusage = 100 - (Get-Counter '\LogicalDisk(C:)\% Idle Time').CounterSamples.CookedValue

  #NETWORK (Kbps)
  $netsent = ((Get-Counter "\Network Interface(*)\Bytes Sent/sec").CounterSamples |Measure-Object -Property CookedValue -Sum).Sum * 8 / 1000
  $netrecv = ((Get-Counter "\Network Interface(*)\Bytes Received/sec").CounterSamples |Measure-Object -Property CookedValue -Sum).Sum * 8 / 1000

  $outstr = "CPU {0,6:N2}%  RAM {1,6:N2}%  DISK(C:) {2,6:N2}%  NET(S) {3,8:N2} Kbps  NET(R) {4,8:N2} Kbps" -f `
    $cpu, $ramusage, $diskusage, $netsent, $netrecv
  Write-Host "`r$outstr" -NoNewLine
  Start-Sleep -Seconds 3
}

プレーンテキストで表示、ダウンロード)

画面7 CUIで動作する簡易的なパフォーマンスモニター (注: タスクマネージャーとは取得方法やタイミングが異なるため、数値は完全には一致しません)

画面7 CUIで動作する簡易的なパフォーマンスモニター
(注: タスクマネージャーとは取得方法やタイミングが異なるため、数値は完全には一致しません)

 

 セイテク・シス管道場(Web) (1) (2)(3)(4)(5)(6)(7)|(8) 

blog_yamanxworld_subscribe

blog_yamanxworld_comment

blog_yamanxworld_WP_ws2025

最新記事