Joe Blog

当您的Linux机器内存不足时,内核会调用内存OOM Killer来释放一些内存。在运行大量内存密集型进程的服务器上经常会遇到这种情况。

在本文中,我们将更深入地了解何时调用OOM Killer,如何确定要杀死哪个进程以及是否可以防止它杀死重要的进程(如数据库)。

Linux内核会为每个正在运行的进程提供一个分数,以oom_score显示在可用内存不足的情况下终止该进程的可能性。分数与该进程使用的内存量成正比。分数是10 x percent of memory used by process。因此,最大分数为100%x 10 =1000。此外,如果进程以特权用户身份运行,则与普通用户进程使用相同的内存相比,该进程的oom_score略低。在Linux的早期版本(v2.6.32内核)中,有一种更为详尽的启发式算法可以计算出该分数。

oom_score过程可以在找到/proc目录。假设您的流程的流程ID(pid)为42,cat /proc/42/oom_score将为您提供该流程的得分。

OOM Killer进行检查oom_score_adj以调整其最终计算出的分数。该文件存在于中/proc/$pid/oom_score_adj。您可以在此文件中添加较大的负分数,以确保您的进程被OOM Killer选择和终止的机会降低。的范围oom_score_adj可以从-1000到1000。如果您为其分配-1000,则它可以使用100%的内存,并且仍然避免被OOM Killer终止。另一方面,如果您为其分配1000,则即使内核使用最少的内存,Linux内核也会继续杀死该进程。

如何更改进程的oom_score_adj

sudo echo -200 > /proc/$pid/oom_score_adj

我们需要以root用户身份执行此操作,或者sudo因为Linux不允许普通用户降低OOM分数。您可以在没有任何特殊权限的情况下以普通用户身份提高OOM分数。例如echo 100 > /proc/42/oom_score_adj

显示所有正在运行的进程的OOM分数

#!/bin/bash
#    Displays running processes in descending order of OOM score
#      (skipping those with both score and adjust of zero).
#    https://dev.to/rrampage/surviving-the-linux-oom-killer-2ki9

contents-or-0 () { if [ -r "$1" ] ; then cat "$1" ; else echo 0 ; fi ; }

{
    header='# %8s %7s %9s %5s %5s %5s  %s\n'
    format="$(echo "$header" | sed 's/^./ /')"
    declare -a lines output
    IFS=$'\r\n' command eval 'lines=($(ps -e -o user,pid,rss))'
    shown=0 ; omits=0
    for n in $(eval echo "{1..$(expr ${#lines[@]} - 1)}") ; do # 1..skip header
        line="${lines[$n]}"
        case "$line" in *[0-9]*)
            set $line ; user=$1 ; pid=$2 ; rss=$3 ; shift 3
            oom_score=$(    contents-or-0  /proc/$pid/oom_score)
            oom_adj=$(      contents-or-0  /proc/$pid/oom_adj)
            oom_score_adj=$(contents-or-0  /proc/$pid/oom_score_adj)            
            if [ -f /proc/$pid/oom_score ] && \
               [ 0 -ne $oom_score -o 0 -ne $oom_score_adj -o 0 -ne $oom_adj ]
            then
                output[${#output[@]}]="$( \
                   printf "$format" \
                          "$user" \
                          "$pid" \
                          "$rss" \
                          "$oom_score" \
                          "$oom_score_adj" \
                          "$oom_adj" \
                          "$(cat /proc/$pid/cmdline | tr '\0' ' ' )" \
                )"
                (( ++shown ))
            else
                (( ++omits ))
            fi
            ;;
        esac
    done
    printf "$header"   ''   '' '' OOM   OOM   OOM ''
    printf "$header" User PID RSS Score ScAdj Adj \
        "Command (shown $shown, omits $omits)"
    for n in $(eval echo "{0..$(expr ${#output[@]} - 1)}") ; do
        echo "${output[$n]}"
    done | sort -k 4nr -k 5rn
}

#----eof

检查您的任何进程是否已被OOM杀死

最简单的方法是grep查看系统日志。在Ubuntu中:grep -i kill /var/log/syslog。如果某个进程被终止,您可能会得到如下结果my_process invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

调整OOM分数的警告

请记住,OOM是更大问题的征兆-可用内存不足。解决此问题的最佳方法是通过增加可用内存(例如,更好的硬件)或将某些程序移动到其他计算机,或通过减少程序的内存消耗(例如,在可能的情况下分配更少的内存)。

OOM调整分数的太多调整将导致随机进程被杀死并且无法释放足够的内存。

实验示例

root@v2ryvps:/tmp# bash display_oom_score.sh 
#                              OOM   OOM   OOM  
#     User     PID       RSS Score ScAdj   Adj  Command (shown 19, omits 46)
      root   28445     49800    49     0     0  /usr/bin/v2ray/v2ray -config /etc/v2ray/config.json 
      root   26577     21584    21     0     0  python /usr/local/shadowsocks/server.py -c /etc/shadowsocks.json -d start 
      root    2310     10360    10     0     0  /lib/systemd/systemd-journald 
      root   21305      8412     8     0     0  sshd: root@pts/1     
      root   27793      7460     7     0     0  /root/trojan/src/trojan -l /root/trojan-access.log -c /root/trojan/src/server.conf 
      root   21311      5204     5     0     0  -bash 
      root     419      5780     5     0     0  /lib/systemd/systemd-logind 
  systemd+    2306      5360     5     0     0  /lib/systemd/systemd-timesyncd 
      root     409      4452     4     0     0  /usr/sbin/rsyslogd -n -iNONE 
      root     488      4808     4     0     0  /lib/systemd/systemd --user 
      root    5720      4404     4     0     0  nginx: worker process                            
      root   16059      3100     3     0     0  bash display_oom_score.sh 
      root    4096      3044     3     0     0  nginx: master process /usr/sbin/nginx -g daemon on; master_process on; 
      root    3575      2644     2     0     0  /usr/sbin/cron -f 
      root     489      2100     2     0     0  (sd-pam) 
      root     481      1380     1     0     0  /sbin/agetty -o -p -- \u --noclear tty1 linux 
  message+     422      4136     0  -900   -15  /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only 
      root    3657      4424     0 -1000   -17  /lib/systemd/systemd-udevd 
      root    4503      6644     0 -1000   -17  /usr/sbin/sshd -D 

这个示例可以看出目前oom_score 分数最高是v2ray pid:28445进程,它的oom_scoreAdj为0 oom_Adj也为0

说明当这个vps的内存使用量不足时会优先把v2ray进程杀死来释放相应的内存,还有一个现象就是sshd 进程具有比较特殊的地位,它的oom_scoreAdj为-1000 oom_Adj为-17 说明sshd进程永远不能被OOM Killer杀死