oom-killer, 殺掉進程的凶手


今天發現進程一直被殺掉,幾經排查,最后確認是被oom-killer殺掉了。

在內核檢測到系統內存不足后,會觸發oom-killer,挑選最占用內存的進程殺掉。

Linux 分配內存策略

Linux內核根據應用程序的要求來分配內存,由於進程實際上並不會將分配的內存全部使用,所以,為了提高性能,內核采用了一種過度分配內存(over-commit-memory)的策略,來間接利用進程的空閑內存,提高內存的使用效率。一般來說,這沒問題。但如果大多數進程都耗光自己的內存,就有麻煩了。因此此時,所有應用程序的內存之和大於物理內存。所以,必須殺掉一部分進程,一般來說,是選內存占用最大的進程殺掉。

挑選原理

挑選的過程由linux/mm/oom_kill.c里的 oom_badness() 函數決定,挑選的算法很直接:是那個最占用內存的進程。
/**
 * oom_badness - heuristic function to determine which candidate task to kill
 * @p: task struct of which task we should calculate
 * @totalpages: total present RAM allowed for page allocation
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
			  const nodemask_t *nodemask, unsigned long totalpages)
{
	long points;
	long adj;

	if (oom_unkillable_task(p, memcg, nodemask))
		return 0;

	p = find_lock_task_mm(p);
	if (!p)
		return 0;

	adj = (long)p->signal->oom_score_adj;
	if (adj == OOM_SCORE_ADJ_MIN) {
		task_unlock(p);
		return 0;
	}

	/*
	 * The baseline for the badness score is the proportion of RAM that each
	 * task's rss, pagetable and swap space use.
	 */
	points = get_mm_rss(p->mm) + p->mm->nr_ptes +
		 get_mm_counter(p->mm, MM_SWAPENTS);
	task_unlock(p);

	/*
	 * Root processes get 3% bonus, just like the __vm_enough_memory()
	 * implementation used by LSMs.
	 */
	if (has_capability_noaudit(p, CAP_SYS_ADMIN))
		adj -= 30;

	/* Normalize to oom_score_adj units */
	adj *= totalpages / 1000;
	points += adj;

	/*
	 * Never return 0 for an eligible task regardless of the root bonus and
	 * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
	 */
	return points > 0 ? points : 1;
}

避免被殺掉的辦法

從上面的代碼里可以看到 oom_badness() 給每個進程打分,根據 points 的高低來決定殺哪個進程,分數越低越不會被殺掉。
這個 points 可以根據 adj 調節,root 權限的進程通常被認為很重要,不應該被輕易殺掉,所以打分的時候可以得到 3% 的優惠(adj -= 30; 分數越低越不容易被殺掉)。
我們可以在用戶空間通過操作每個進程的 oom_adj 內核參數來使得進程不容易被 OOM killer 選中殺掉。比如,如果不想 test進程被輕易殺掉的話可以找到 test運行的進程號后,調整 oom_score_adj 為 -15(注意 points 越小越不容易被殺):

# ps aux | grep test
test    2334  1.6  2.1 623800 4876 ?        Ssl  09:52   0:00 /usr/sbin/test

# cat /proc/2334/oom_score_adj
0
# echo -15 > /proc/2334/oom_score_adj

當然,也可以完全關閉 OOM killer,但線上生產環境最好不要這么做。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM