Linux?內核有個機制叫OOM?killer(Out-Of-Memory?killer),該機制會監控那些占用內存過大,尤其是瞬間很快消耗大量內存的進程,為了防止內存耗盡而內核會把該進程殺掉。典型的情況是:某天一臺機器突然ssh遠程登錄不了,但能ping通,說明不是網絡的故障,原因是sshd進程被OOM?killer殺掉了(多次遇到這樣的假死狀況)。重啟機器后查看系統日志/var/log/messages會發現Out?of?Memory:?Kill?process?1865(sshd)類似的錯誤信息。
防止重要的系統進程觸發(OOM)機制而被殺死:可以設置參數/proc/PID/oom_adj為-17,可臨時關閉linux內核的OOM機制。內核會通過特定的算法給每個進程計算一個分數來決定殺哪個進程,每個進程的oom分數可以/proc/PID/oom_score中找到。我們運維過程中保護的一般是sshd和一些管理agent。
?
保護某個進程不被內核殺掉可以這樣操作:
點擊(此處)折疊或打開
echo -17 > /proc/$PID/oom_adj
如何防止sshd被殺,可以這樣操作:
點擊(此處)折疊或打開
pgrep -f "/usr/sbin/sshd" | while read PID;do echo -17 > /proc/$PID/oom_adj;done
點擊(此處)折疊或打開
#/etc/cron.d/oom_disable
*/1**** root pgrep -f "/usr/sbin/sshd" | while read PID;do echo -17 > /proc/$PID/oom_adj;done
點擊(此處)折疊或打開
echo -17 > /proc/$(pidof sshd)/oom_adj
至于為什么用-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的分數越高就越會被內核優先殺掉。
點擊(此處)折疊或打開
vm.panic_on_oom = 1 //1表示關閉,默認為0表示開啟OOM
?
# sysctl -p
為了驗證OOM機制的效果,我們不妨做個測試。
首先看看我系統現有內存大小,沒錯96G多,物理上還要比查看的值大一些。
再看看目前進程最大的有哪些,top查看,我目前只跑了兩個java程序的進程,分別4.6G,再往后redis進程吃了21m,iscsi服務占了32m,gdm占了25m,其它的進程都是幾M而已。
現在我自己用C寫一個叫bigmem程序,我指定該程序分配內存85G,呵呵,效果明顯,然后執行后再用top查看,排在第一位的是我的bigmem,RES是物理內存,已經吃滿了85G。
繼續觀察,當bigmem穩定保持在85G一會后,內核會自動將其進程kill掉,增長的過程中沒有被殺,如果不希望被殺可以執行
點擊(此處)折疊或打開
pgrep -f "bigmem" | while read PID; do echo -17 > /proc/$PID/oom_adj;done
執行以上命令前后,明顯會對比出效果,就可以體會到內核OOM機制的實際作用了。
?
注意:
1.Kernel-2.6.26之前版本的oomkiller算法不夠精確,RHEL?6.x版本的2.6.32可以解決這個問題。
2.子進程會繼承父進程的oom_adj。
3.OOM不適合于解決內存泄漏(Memory?leak)的問題。
4.有時free查看還有充足的內存,但還是會觸發OOM,是因為該進程可能占用了特殊的內存地址空間。