発生した問題
状況
ある日、PyTorch で学習スクリプトを実行した際、Segmentation fault (core dumped) が発生して学習が強制終了した。その後も機械学習プログラムを走らせると、序盤で同様のエラーが再現するようになり、再起動やキャッシュクリアでは改善しなかった。さらに、Dockerfile を用いたイメージビルドや C/CUDA モジュールのビルド中にも同じ Segmentation Fault が発生し、PCもフリーズするようになり絶望した。
Segmentation Fault (セグフォ) とは?
プロセスがアクセス権のないメモリアドレスに触れたとき、カーネルが送る強制終了シグナル のこと。C/C++ や Python/CUDA 拡張がポインタを誤操作したとき、あるいは壊れたハードウェアが不正値を返した場合にも発生するらしい。様々な要因があるが、ユーザー空間からは単なる "Segmentation fault (core dumped)" という一行でしか見えないため非常に厄介。
環境
| 項目 | バージョン |
|---|---|
| OS | Ubuntu 22.04.1 LTS |
| Kernel | 6.8.0-62-generic |
| CPU | Intel(R) Core(TM) i9-14900KF |
| GPU | NVIDIA RTX 6000 Ada |
原因特定
Segmentation Faultの原因は大きく3つに整理できる。
-
リソース枯渇
- 例:RAM / VRAM 不足
-
ソフトウェア依存関係(ユーザー空間)
- 例:NumPy ↔︎ PyTorch ↔︎ CUDA 拡張の ABI 不整合、GCC バージョン違い
-
ドライバ/ハードウェア層
- 例:NVIDIA DKMS ビルド失敗、壊れた DIMM や CPU コア、GPU 過熱
以下に、私が原因特定までにたどった手順を示す。
1. リソースの確認
まず、最も特定が簡単であろうリソースの確認を行った。
RAM使用状況の確認
free -h
VRAM使用状況の確認
nvidia-smi
これらの出力を確認したが、プログラムを実行しても異常な数値になることはなく、問題のある部分は見当たらなかったため、1.リソース枯渇 が直接的な原因ではないと判断した。
2. gdbでコアダンプを確認
次に、ソフトウェアによる問題があるか確認するために、gdbと呼ばれるものを用いてエラーの原因の詳細な解析を行った。
gdbとは?
GNU Debugger の略で、Linux/Unix で広く使われるデバッガ。コアダンプや実行中プロセスを解析し、クラッシュ時のスタックトレースや変数の状態を確認できる。
まず、ulimit -c unlimited コマンドを入力する。デフォルトでは容量やセキュリティの観点からコアダンプの内容が保存されないが、このコマンドを実行することで保存されるコアダンプファイルのサイズ制限を無制限にし、Segmentation Faultが発生した際の詳細な情報を取得できるようになる。
その後、gdb --args python <問題が起こるスクリプト>.py の後に(gdb) runを実行し、エラーを発生させ、コアダンプファイルを生成させる。
gdb --args python <problem_script>.py
(gdb) run
# プログラムが実行され、Segmentation Faultが発生
Segmentation Faultが発生した時点で、gdb内でbt(backtrace)コマンドを実行してスタックトレースを確認する。
(gdb) bt
私の場合、一番上に以下のように出力された。
#0 0x00007d044e3f2340 in ?? () from /usr/lib/x86_64-linux-gnu/libcuda.so.1
これは、CUDA ドライバ(libcuda.so.1)内、すなわちGPU または GPU ドライバ・ランタイム周りの問題が考えられるらしい。
3. バージョン確認
CUDAドライバが原因の可能性が高いことが分かったため、次にシステム内のCUDA関連のバージョン情報を確認した。
NVIDIAドライバのバージョン確認
nvidia-smi
CUDAランタイムのバージョン確認
nvcc -V
調べてみたところ、上記のバージョンの互換性は特に問題なかった。念のため他のバージョンのNVIDIAドライバやCUDAを入れたが、治らなかった。まれにpytorchやnumpyのバージョン不整合でsegmentation faultが出るらしいので、他のバージョンを入れなおしてみたが、改善しなかった。
ほかにもいろいろとソフトウェアのバージョンを変えてやってみたが変化がなかったため、2. ソフトウェア依存関係はエラーの原因ではないと推測。
4. ハードウェア(メモリ、CPU)のエラー確認
1~3を踏まえて、ハードウェア側に問題がある可能性が高いと考え、メモリとCPUの検証を行った。GPUについては検証を行う前にエラーが特定できたので、今回はGPUの物理的な検証は実施していない。
参考:GPUのハードウェア検証方法
もしGPUのハードウェア自体に問題がないかを検証したい場合は、GPU-Burnというツールが有効らしい。GPU-BurnはGPU専用のストレステストツールで、CUDA計算による高負荷をかけてGPUの物理的な安定性を確認できる。
メモリの検証
まず、メモリ(RAM)に問題がないかを確認するために、memtestと呼ばれるツールを使用した。
memtestとは?
メモリの物理的な欠陥や不安定性を検出するためのテストツール。メモリの全領域に対して様々なパターンでデータの書き込み・読み込みを行い、データの整合性を確認することでメモリエラーを発見できる。
具体的なやり方については、こちらの記事が非常にわかりやすかったため、これを見てテストを行った。その結果、エラーは発生せず、メモリに物理的な問題がある可能性が低いことが分かった。
CPUの検証
次に、CPUの動作確認を行うため、stress-ngでCPUストレステストを実行した。
stress-ngとは?
システムのハードウェアコンポーネント(CPU、メモリ、GPU、ディスクI/Oなど)に意図的に高負荷をかけて動作テストを行うLinux用のストレステストツール。ハードウェアの安定性確認や、熱設計の検証、システムの限界性能測定などに使用される。
# 10分間のCPUストレステスト
stress-ng --cpu $(nproc) --timeout 10m
--cpu で負荷をかけるCPUコア数、--timeoutで負荷をかける時間を指定できる。また、$(nproc)でコア数を自動で取得してくれる。私のサーバーでは32コアのCPUに対して10分のストレステストを行った。
その結果、コマンド実行直後に以下が出力された。
stress-ng: info: [23970] setting to a 600 second (10 mins, 0.00 secs) run per stressor
stress-ng: info: [23970] dispatching hogs: 32 cpu
stress-ng: info: [24001] stressor terminated with unexpected signal signal 8 'SIGFPE'
stress-ng: error: [23970] process 24001 (stress-ng-cpu) terminated with an error, exit status=5 (killed by signal)
stress-ng: info: [23970] unsuccessful run completed in 1.15s
stress-ng: fail: [23970] cpu instance 30 corrupted bogo-ops counter, 1591 vs 0
stress-ng: fail: [23970] cpu instance 30 hash error in bogo-ops counter and run flag, 715004559 vs 0
stress-ng: fail: [23970] metrics-check: stressor metrics corrupted, data is compromised
これらのエラーは、単純なCPU計算でもシステムレベルの問題が発生していることを示しており、CPUに課題がある可能性が高まった。
そこで、stress-ngを行っている最中のCPUの動作を確認するために、以下のコマンドを使用した。
watch -n1 sensors
これは、システムの温度センサーやファンの回転数、電圧などのハードウェア情報をリアルタイムで監視するLinuxコマンドであり、watch -n1と組み合わせることで、1秒間隔でセンサー情報を継続的に更新表示できる。
上記のコマンドを実行した状態で、同様にストレスチェックを行った結果、実行直後にセンサー情報の一部が以下のようになった。
Package id 0: +100.0°C (high = +80.0°C, crit = +100.0°C)
Core 0: +89.0°C (high = +80.0°C, crit = +100.0°C)
Core 4: +90.0°C (high = +80.0°C, crit = +100.0°C)
Core 8: +95.0°C (high = +80.0°C, crit = +100.0°C)
Core 12: +94.0°C (high = +80.0°C, crit = +100.0°C)
Core 16: +74.0°C (high = +80.0°C, crit = +100.0°C)
Core 20: +100.0°C (high = +80.0°C, crit = +100.0°C)
Core 24: +96.0°C (high = +80.0°C, crit = +100.0°C)
Core 28: +100.0°C (high = +80.0°C, crit = +100.0°C)
Core 32: +89.0°C (high = +80.0°C, crit = +100.0°C)
Core 33: +89.0°C (high = +80.0°C, crit = +100.0°C)
Core 34: +89.0°C (high = +80.0°C, crit = +100.0°C)
Core 35: +89.0°C (high = +80.0°C, crit = +100.0°C)
Core 36: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 37: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 38: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 39: +92.0°C (high = +80.0°C, crit = +100.0°C)
Core 40: +74.0°C (high = +80.0°C, crit = +100.0°C)
Core 41: +74.0°C (high = +80.0°C, crit = +100.0°C)
Core 42: +74.0°C (high = +80.0°C, crit = +100.0°C)
Core 43: +74.0°C (high = +80.0°C, crit = +100.0°C)
Core 44: +72.0°C (high = +80.0°C, crit = +100.0°C)
Core 45: +72.0°C (high = +80.0°C, crit = +100.0°C)
Core 46: +73.0°C (high = +80.0°C, crit = +100.0°C)
Core 47: +73.0°C (high = +80.0°C, crit = +100.0°C)
この結果から、多くのCPUコアでhighの基準である80℃を超え、さらに一部のコアで臨界温度(強制シャットダウンとなりうる温度)である100℃を超えていることが判明した。これらのデータから、CPUの冷却システムの問題が原因であり、Segmentation FaultやPCのフリーズは、「CPU 過熱 → 演算エラー → 異常メモリアクセス → カーネルが SIGSEGV を送出」の流れによって発生する可能性が高いことが判明した。
解決策
CPUが異常に加熱する原因を調べた結果、最新のCPUの性能にPCの冷却が追い付かずに、熱暴走を起こすことがあるらしい。購入してから約1年の新しめのPCだが、比較的大規模な学習を続けていたため冷却能力が低下してしまったのかも?
このような問題を解決するためには、2つのアプローチがある。
1. 冷却能力を強化する:物理的な冷却システムの改善
- CPUクーラーの交換:空冷から水冷への変更、またはより高性能なクーラーへの交換
- ケースファンの増設:適切なエアフロー確保のため吸気・排気ファンを追加
- サーマルペーストの塗り直し:経年劣化したサーマルペーストを高品質なものに交換
- ケース内清掃:ホコリ除去によるエアフロー改善
- ケース自体の交換:より冷却効率の高いケースへの変更
2. 発熱を抑える:ソフトウェア的な制御による発熱抑制
- CPU周波数の制限:最大クロック周波数を下げることで消費電力を削減
- CPU使用率の制限:同時実行するプロセス数やスレッド数を制限
- 電力管理設定の調整:省電力モードの活用
- 負荷分散の実装:長時間の高負荷処理を分割して実行
私はハードウェアについては疎く、物理的に変えようとするとさらに悪化しそうだったため2.のソフトウェアでCPUの制御をかけるアプローチを実施することにした。具体的にはCPUの最大周波数を制限し、発熱量を抑制する方法を採用した。これを実施するにあたって、こちらの記事が非常に参考になった。
以下のコマンドで周波数の変更を行った。
# 1) 念のため元ファイルをバックアップ
sudo cp /etc/default/cpupower /etc/default/cpupower.bak.$(date +%Y%m%d-%H%M%S)
# 2) 新しい設定を書き込む
echo 'CPUPOWER_START_OPTS="frequency-set -g powersave --max 5.13GHz"' \
| sudo tee /etc/default/cpupower
# 3) systemd に設定変更を通知し、即座にサービスを再起動
sudo systemctl daemon-reload
sudo systemctl restart cpupower.service
私のサーバでは最大周波数が5.7GHzとなっていたので、90%の5.13GHzに制限することで発熱を抑制しつつ、大幅な性能低下を避けることを狙った。
設定後、cpufreq-infoコマンドを入力し、制限が適用されているか確認。
analyzing CPU 0:
driver: intel_pstate
CPUs which run at the same hardware frequency: 0
CPUs which need to have their frequency coordinated by software: 0
maximum transition latency: 4294.55 ms.
hardware limits: 800 MHz - 5.70 GHz
available cpufreq governors: performance, powersave
current policy: frequency should be within 800 MHz and 5.13 GHz.
The governor "powersave" may decide which speed to use
within this range.
current CPU frequency is 5.10 GHz.
出力の一部が ↑ のように表示され、正しく制限できていることが確認できた。
その後、CPUの検証の際と同様にstress-ngによる負荷テストを行った結果、エラーが出なくなった!!!さらに、segmentation faultが出ていたpython スクリプトを実行してみた結果、最後まで学習を終えることができた。学習を行っている際にwatch -n1 sensors でCPUの温度を確認した。
Every 1.0s: sensors nougata-pc8: Thu Jun 19 08:44:59 2025
coretemp-isa-0000
Adapter: ISA adapter
Package id 0: +66.0°C (high = +80.0°C, crit = +100.0°C)
Core 0: +47.0°C (high = +80.0°C, crit = +100.0°C)
Core 4: +45.0°C (high = +80.0°C, crit = +100.0°C)
Core 8: +53.0°C (high = +80.0°C, crit = +100.0°C)
Core 12: +46.0°C (high = +80.0°C, crit = +100.0°C)
Core 16: +63.0°C (high = +80.0°C, crit = +100.0°C)
Core 20: +61.0°C (high = +80.0°C, crit = +100.0°C)
Core 24: +53.0°C (high = +80.0°C, crit = +100.0°C)
Core 28: +68.0°C (high = +80.0°C, crit = +100.0°C)
Core 32: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 33: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 34: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 35: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 36: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 37: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 38: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 39: +50.0°C (high = +80.0°C, crit = +100.0°C)
Core 40: +46.0°C (high = +80.0°C, crit = +100.0°C)
Core 41: +46.0°C (high = +80.0°C, crit = +100.0°C)
Core 42: +46.0°C (high = +80.0°C, crit = +100.0°C)
Core 43: +46.0°C (high = +80.0°C, crit = +100.0°C)
Core 44: +47.0°C (high = +80.0°C, crit = +100.0°C)
Core 45: +47.0°C (high = +80.0°C, crit = +100.0°C)
Core 46: +47.0°C (high = +80.0°C, crit = +100.0°C)
Core 47: +47.0°C (high = +80.0°C, crit = +100.0°C)
全てのCPUコアが安全な温度になった。
周波数を制限することのデメリットとして処理能力が低下するらしいが、GPUによる機械学習がメインの用途であればCPUの性能低下による影響は限定的であり、実際に学習にかかる時間にほとんど変化は見られなかったため、この対策は適切であったと考えている。
まとめ
今回のSegmentation Faultは、以下の流れで解決することができた:
1. リソースの確認: RAMの性能不足は確認できず
2. gdbによる原因調査: CUDAドライバ内でのエラーを発見したが、根本原因ではないと判断
3. ハードウェア検証: memtestにより、メモリに故障はないと判断。CPUストレスチェックにより温度異常(100°C)を特定
4. 根本原因の解明: 冷却能力不足による熱暴走
5. 解決策の実施: CPU周波数制限による発熱抑制
6. 効果の確認: 温度正常化とエラー解消
結果的にハードウェア起因の問題であったが、ソフトウェア的な制御によって解決できた。ハードウェアが破損していたらお手上げ状態なので、一安心した。
Segmentation Faultのエラーデバッグもうちょっと簡単にできてもいいんじゃない?と思った。ハードウェア起因かソフトウェア起因かどちらか分からずにSegmentation Faultのエラー1行だけで解決しろってのはかなり厳しい。
参考文献・記事
https://github.com/wilicc/gpu-burn
Memtest86の使い方
cpupowerでCPU周波数を動的に変更する