OOM killer 機制就是Linux殺死進程的以個機制,如果進程損壞系統資源到底某種程度,OOM killer 機制就會強制殺死進程。本文就來介紹一下Linux如何使用OOM killer 機制。
簡單來說該機制會監控那些占用內存過大,尤其是瞬間很快消耗大量內存的進程,為了防止內存耗盡而內核會把該進程殺掉。
這個功能即使在無法釋放內存的情況下,也能夠重復進行確保內存的處理過程,防止系統停滯,還可以找出過度消耗內存的進程。
典型的情況是:某天一台機器突然ssh遠程登錄不了,但能ping通,說明不是網絡的故障或者機器down掉,很大可能是sshd進程被 OOM killer殺掉了。
重啟機器後查看系統日志/var/log/messages會發現 Out of Memory: Kill process 247(sshd)類似的錯誤信息。
還有另外一種情況也會導致能ping不能ssh,就是網絡連接過多把系統文件描述符資源耗盡,這裡暫時不考慮這種情況。
而在使用vip的高可用方案中,這種情況也很容易出現腦裂的現象。
防止重要的系統進程觸發(OOM)機制而被殺死:可以設置參數/proc/PID/oom_adj為-17,可臨時關閉linux內核的OOM機制。內核會通過特定的算法給每個進程計算一個分數來決定殺哪個進程,每個進程的oom分數可以/proc/PID/oom_score中找到。
我們認為重要的進程有sshd,或者一些監控守護進程,大家可以根據自己實際情況選擇需要保護的進程。
保護某個進程不被內核殺掉可以這樣操作:
echo -17 》 /proc/$PID/oom_adj
可以寫一個簡單的腳本,部署在crontab上防止重要進程被oom
pgrep -f “/usr/sbin/sshd” | while read PID;do echo -17 》 /proc/$PID/oom_adj;done
其中的“/usr/sbin/sshd”可以替換為你認為重要的進程,不過在匹配時候注意不要匹配錯了
1.進程的選定方法
OOM Killer在內存耗盡時,會查看所有進程,並分別為每個進程計算分數。將信號發送給分數最高的進程。
2.計算分數的方法
OOM Killer計算分數
1. 首先,計算分數時是以進程的虛擬內存大小為基准的,虛擬內存大小可以使用ps命令的VSZ或/proc/《PID》/status的 VmSize來確認。對於正在消耗虛擬內存的進程,其最初的得分較高,單位是將1KB作為1個得分,消耗1GB內存的進程,得分約為1024*1024。
2.如果進程正在執行swapoff系統調用,則得分設置為最大值(unsigned long的最大值)。這是因為禁用swap的行為與消除內存不足是相反的,會立刻將其作為OOM Killer的對象進程。
3.如果是母進程,則將所有子進程內存大小的一半作為分數。
4. 根據進程的CPU使用時間和進程啟動時間調整得分,這是因為在這裡認為越是長時間運行或從事越多工作的進程越重要,需保持得分較低。
5.對於通過nice命令等將優先級設置得較低的進程,要將得分翻倍。nice-n中設置為1~19的命令的得分翻倍。
6.特權進程普遍較為重要,因此將其得分設置為1/4。
7.通過capset(3)等設置了功能(capability)CAP_SYS_RAWIO注3的進程,其得分為1/4,將直接對硬件進行操作的進程判斷為重要進程。
8.關於Cgroup,如果進程只允許與促使OOM Killer運行的進程所允許的內存節點完全不同的內存節點,則其得分為1/8。
9.最後通過proc文件系統oom_adj的值調整得分。
依據以上規則,為所有進程打分,向得分最高的進程發送信號SIGKILL(到Linux 2.6.10為止,在設置了功能CAP_SYS_RAWIO的情況下,發送SIGTERM,在沒有設置的情況下,發送SIGKILL)。
各進程的得分可以使用/proc/《PID》/oom_score來確認。
但是init(PID為1的)進程不能成為OOM Killer的對象。當成為對象的進程包含子進程時,先向其子進程發送信號。
向成為對象的進程發送信號後,對於引用系統的全線程,即使線程組(TGID)不同,如果存在與對象進程共享相同內存空間的進程,則也向這些進程發送信號。
至於為什麼用-17而不用其他數值(默認值為0),這個是由linux內核定義的,查看內核源碼可知:
以linux- 3.3.6版本的kernel源碼為例,路徑為linux-3.6.6/include/linux/oom.h,閱讀內核源碼可知oom_adj的可調 值為15到-16,其中15最大-16最小,-17為禁止使用OOM。oom_score為2的n次方計算出來的,其中n就是進程的oom_adj值,所 以oom_score的分數越高就越會被內核優先殺掉。
當然還可以通過修改內核參數禁止OOM機制
# sysctl -w vm.panic_on_oom=1
vm.panic_on_oom = 1 //1表示關閉,默認為0表示開啟OOM
# sysctl -p
測試程序
命令行參數輸入占用內存大小N,根據自身實驗環境的物理內存大小來設置,例如我的實驗環境為內存4G,設為4G就足夠了
代碼命名為mem.c,編譯方法 gcc -o mem mem.c
#include 《stdio.h》
#include 《stdlib.h》
#include 《string.h》
#define PAGE_SZ (1《《12)
int main(int argc, char* argv[]) {
int i;
if (argc != 2) return 0;
int gb = atoi(argv[1]);
for (i = 0; i 《 ((unsigned long)gb《《30)/PAGE_SZ ; ++i) {
void *m = malloc(PAGE_SZ);
if (!m)
break;
memset(m, 0, 1);
}
printf(“allocated %lu MB\n”, ((unsigned long)i*PAGE_SZ)》》20);
getchar();
return 0;
}
然後執行 。/mem 4
如果不執行任何操作的話,直接運行結果會發現系統自動oom掉這個進程
如果我們進行以下操作,把進程優先級設置為-17
pgrep -f “mem” | while read PID; do echo -17 》 /proc/$PID/oom_adj;done
你會發現系統不會把這個占用大內存的進程oom掉,但這時你也會發現系統響應變慢甚至宕機!
設置任意進程觸發oom
一個最簡單的測試觸發OOM的方法,可以把某個進程的oom_adj設置到15(最大值),最容易觸發。然後執行以下命令:
echo f 》 /proc/sysrq-trigger
以上就是Linux如何使用OOM killer 機制,Linux強制殺死進程就不會出現死機或者中毒的現象,這是Linux一大優點。