內存兌付失敗時,Linux 由 oom_kill_process() 選擇犧牲者。選擇依據是 oom_badness() 計算的分數(/proc/),分數越高越先死。本文逐行解讀內核源碼邏輯,實驗驗證 oom_score_adj(-1000~1000)對生存率的決定性影響,為 MySQL 免殺奠基。
points = p->mm->total_vm; // 1. 基礎分 = 進程物理頁數(RSS,不含 Swap)
points += 子進程獨立 mm 的 total_vm; // 2. 累加子進程物理內存
points /= sqrt(cpu_time); // 3. CPU 時間長 → 分數降低(保護長運行進程)
points /= sqrt(sqrt(run_time)); // 4. 存活時間長 → 分數降低(保護系統服務)
if (task_nice(p) > 0) points *= 2; // 5. 低優先級 → 分數翻倍
if (capable(CAP_SYS_ADMIN) || uid==0) points /= 4; // 6. root/特權進程 → 分數降低
if (capable(CAP_SYS_RAWIO)) points /= 4; // 7. 直接硬件訪問 → 分數降低
points <<= oom_score_adj; // 8. 用戶態調控:左移 oom_score_adj 位(-1000~1000)
關鍵洞察:
oom_score_adj 為 指數級影響:+1000 必殺、-1000 免死金牌2GB RAM + 400MB Swap,啟動 3 個 1GB 申請+分級寫入進程
第一個達到物理上限的進程被 Kill,oom_score=1000
echo 15 > /proc//oom_score_adj
該進程優先被 Kill,驗證指數級懲罰
echo -1000 > /proc//oom_score_adj
進程存活,但系統可能因無可殺進程而崩潰(極端風險)
| 因子 | MySQL 現狀 | 影響 |
|---|---|---|
| RSS 大 | Buffer Pool 佔物理內存 60-80% | 基礎分極高 |
| 長運行 | 月級存活 | run_time 保護有限(四次方根) |
| root 權限 | 通常 mysql 用戶非 root | 無 root 保護 |
| 子進程 | 無 fork 子進程 | 無額外累加 |
| oom_score_adj | 默認 0 | 無人為保護 |