Linux內核OOM機制的詳細分析和防止進程被OOM殺死的方法

轉載自:http://blog.chinaunix.net/uid-29242873-id-3942763.html

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 -"/usr/sbin/sshd" | while read PID;do echo -17 > /proc/$PID/oom_adj;done

可以在計劃任務里加入這樣一條定時任務,就更安全了:

點擊(此處)摺疊或打開

#/etc/cron.d/oom_disable

*/1**** root pgrep -"/usr/sbin/sshd" | while read PID;do echo -17 > /proc/$PID/oom_adj;done

爲了避免重啓失效,可以寫入/etc/rc.d/rc.local

點擊(此處)摺疊或打開

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的分數越高就越會被內核優先殺掉。

 
當然還可以通過修改內核參數禁止OOM機制


點擊(此處)摺疊或打開

# sysctl -w vm.panic_on_oom=1
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

點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. #define PAGE_SZ (1<<12)
  6.  
  7. int main() {
  8.     int i;
  9.     int gb = 85; //以GB爲單位分配內存大小
  10.  
  11.     for (= 0; i < ((unsigned long)gb<<30)/PAGE_SZ ; ++i) {
  12.         void *= malloc(PAGE_SZ);
  13.         if (!m)
  14.             break;
  15.         memset(m, 0, 1);
  16.     }
  17.     printf("allocated %lu MB\n", ((unsigned long)i*PAGE_SZ)>>20);
  18.     getchar();
  19.     return 0;
  20. }
呵呵,效果明顯,然後執行後再用top查看,排在第一位的是我的bigmem,RES是物理內存,已經吃滿了85G。

 

繼續觀察,當bigmem穩定保持在85G一會後,內核會自動將其進程kill掉,增長的過程中沒有被殺,如果不希望被殺可以執行

點擊(此處)摺疊或打開

pgrep -"bigmem" | while read PID; do echo -17 > /proc/$PID/oom_adj;done

執行以上命令前後,明顯會對比出效果,就可以體會到內核OOM機制的實際作用了。

 

如果你覺得寫C代碼麻煩,我告訴大家另外一個最簡單的測試觸發OOM的方法,可以把某個進程的oom_adj設置到15(最大值),最容易觸發。然後執行以下命令:

點擊(此處)摺疊或打開

  1. echo f > /proc/sysrq-trigger // 'f' - Will call oom_kill to kill a memory hog process.
以下我來觸發mysqld的OOM看看:

需要注意的是這個測試,只是模擬OOM,不會真正殺掉進程

點擊(此處)摺疊或打開

  1. ps -ef | grep mysqld | grep -v grep
查看mysql進程,發現依然存在

注意:

1.Kernel-2.6.26之前版本的oomkiller算法不夠精確,RHEL 6.x版本的2.6.32可以解決這個問題。

2.子進程會繼承父進程的oom_adj。

3.OOM不適合於解決內存泄漏(Memory leak)的問題。

4.有時free查看還有充足的內存,但還是會觸發OOM,是因爲該進程可能佔用了特殊的內存地址空間。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章