Linux 的 Out-of-Memory (OOM) Killer

转载自http://www.dbanotes.net/database/linux_outofmemory_oom_killer.html 

同事在 Linux 服务器上遇到点小问题,我也上去折腾半天。这还是第一次注意到 Linux 这个多年来就存在的特性:OOM Killer 。说白了 OOM Killer 就是一层保护机制,用于避免 Linux 在内存不足的时候不至于出太严重的问题,把无关紧要的进程杀掉,有些壮士断腕的意思。

先要学习点老知识,在 32 位CPU 架构下寻址是有限制的。Linux 内核定义了三个区域:

# DMA: 0x00000000 -  0x00999999 (0 - 16 MB) 
# LowMem: 0x01000000 - 0x037999999 (16 - 896 MB) - size: 880MB
# HighMem: 0x038000000 - <硬件特定>

LowMem 区 (也叫 NORMAL ZONE ) 一共 880 MB,而且不能改变(除非用 hugemem 内核)。对于高负载的系统,就可能因为 LowMem 利用不好而引发 OOM Killer 。一个可能原因是 LowFree 太少了,另外一个原因是 LowMem 里都是碎片,请求不到连续的内存区域【根据我遇到的一个案例,一个猜想是 有些应用一次性请求比较大的内存,恰恰又是 880M 之内的,空闲的(LowFree)不够大,就会触发 OOM Killer 出来干活】。检查当前 LowFree 的值:

# cat /proc/meminfo |grep LowFree 

检查LowMem内存碎片:

# cat /proc/buddyinfo

上面这条命令要在 2.6 Kernel 环境下有效。据说使用 SysRq 的方式更好,不过 Hang 的时候再用吧。参见 Metalink Note:228203.1 。

根据一些文档描述,OOM Killer 在 2.4 与 2.6 上表现是不一样的。2.4 的版本中是把新进来(新申请内存)的进程杀掉。而 2.6 上是杀掉占用内存最厉害的进程(这是很危险的,很容易导致系统应用瘫痪)。

对于 RHEL 4 ,新增了一个参数: vm.lower_zone_protection 。这个参数默认的单位为 MB,默认 0 的时候,LowMem 为 16MB。建议设置 vm.lower_zone_protection = 200 甚至更大以避免 LowMem 区域的碎片,是绝对能解决这个问题的(这参数就是解决这个问题出来的)。

而对于 RHEL 3 (Kernel 2.4) 似乎没什么好办法,一个是用 Hugemem 内核(天知道会不会引入新的毛病),一个是升级到 2.4.21-47 并且使用新的核心参数 vm.vm-defragment 控制碎片的数量。再就是使用 RHEL 4 (Kernel 2.6),这又绕回去了。说白了,如果遇到 OOM Killer ,基本上是低版本 Kernel 设计上有点缺陷。

其它,如果去查询 RedHat 的 Bug 库,会发现不少 Kernel 版本也有 Bug 的。尤其在使用 NFS 的场景。

Tip: OOM Killer 的关闭与激活方式:

# echo "0" > /proc/sys/vm/oom-kill 
# echo "1" > /proc/sys/vm/oom-kill

更多参考信息:



UNIX编程的艺术

 

花了3天的时间略读了一下UNIX编程艺术这本书,对某些概念有了了解,对某些方法有了认识。
1、软件模块之间的正交性,模块之间要做到相互无影响,也就是要尽量解耦
2、软件的透明性,我的理解就是代码的层次很清晰,层次尽量少,尽量薄
3、软件的紧凑性,按照作者的说法,就是要围绕一个中心来组织代码,比如一个强大的算法
4、程序之间的通信协议尽量采用文本数据而不是二进制数据,这样有几个好处,容易理解、扩展方便、调试方便。如果认为效率较低占用更多带宽,可以压缩。
5、 尽量避免使用多线程的方法。UNIX的工程哲学是采用多个进程,多个进程间通过管道、文本协议、标准输入输出来相互协作完成工作,就好像一条流水线。在单个进程内,可以利用select、poll这类多路复用技术避免多线程(还有就是nonblock操作)。
6、脚本语言的妙用,perl、python、ruby,可以在c/c++不擅长的领域发挥作用,有效提高生产率,还可以作为领域语言发挥作用
7、 数据驱动编程的模型。采用文本数据+领域语言+基础模块来完成工作。文本数据描述复杂任务本身,领域语言(由脚本语言编写)构成逻辑层用来解释文本数据并 将复杂任务分解为多个基础任务并调用负责完成基础任务的模块(由c/c++语言编写)。当领域逻辑较简单时也可以省略逻辑层。
8、遵循简单但不能太简单(不能简单到无法完成任务)的原则,尤其是由多个简单的模块而不是由一个复杂的模块来完成任务。
9、复杂度的分类,偶然复杂度--由开发人员的素质决定,比如偶然犯的错误,不好的编程习惯等;选择复杂度--由开发人员所选择的编程语言、建模方法、设计方法所决定;本质复杂度--由任务自身的数据结构、功能需求决定。
10、使用yacc和lex(gnu版本bison、flex)来根据BNF做解析器,对于设计标准协议、解析标准协议十分有效。
11、好好利用emacs的强大功能(今天才知道emacs可以很好的结合cvs使用)。