本节介绍OOM Killer的运行与结构。
Linux中的Out Of Memory(OOM) Killer功能作为确保内存的最终手段,可以在耗尽系统内存或交换区后,向进程发送信号,强制终止该进程。
这个功能即使在无法释放内存的情况下,也能够重复进行确保内存的处理过程,防止系统停滞。还可以找出过度消耗内存的进程。本节将介绍2.6内核的OOM Killer。
确认运行、日志
进行系统验证或负载试验时,有时会出现正在运行中的进程终止或者SSH连接突然断开、尝试重新登录也无法连接的情况。
这时需要查看日志。有时会输出如下内核信息。
1. Pid: 4629, comm: stress Not tainted 2.6.26 #3
2. Call Trace:
3. [<ffffffff80265a2c>] oom_kill_process+0x57/0x1dc
4. [<ffffffff80238855>] __capable+0x9/0x1c
5. [<ffffffff80265d39>] badness+0x16a/0x1a9
6. [<ffffffff80265f59>] out_of_memory+0x1e1/0x24b
7. [<ffffffff80268967>] __alloc_pages_internal+0x320/0x3c2
8. [<ffffffff802726cb>] handle_mm_fault+0x225/0x708
9. [<ffffffff8047514b>] do_page_fault+0x3b4/0x76f
10. [<ffffffff80473259>] error_exit+0x0/0x51
11. Node 0 DMA per-cpu:
12. CPU 0: hi: 0, btch: 1 usd: 0
13. CPU 1: hi: 0, btch: 1 usd: 0
14. ...
15. Active:250206 inactive:251609 dirty:0 writeback:0 unstable:0
16. free:3397 slab:2889 mapped:1 pagetables:2544 bounce:0
17. Node 0 DMA free:8024kB min:20kB low:24kB high:28kB active:8kB inactive:180kB present:7448kB pa
18. ges_scanned:308 all_unreclaimable? yes
19. lowmem_reserve[]: 0 2003 2003 2003
20. ...
21. Node 0 DMA: 6*4kB 4*8kB 2*16kB 2*32kB 5*64kB 1*128kB 3*256kB 1*512kB 2*1024kB 2*2048kB 0*4096k
22. B = 8024kB
23. Node 0 DMA32: 1*4kB 13*8kB 1*16kB 6*32kB 2*64kB 2*128kB 1*256kB 1*512kB 0*1024kB 0*2048kB 1*40
24. 96kB = 5564kB
25. 29 total pagecache pages
26. Swap cache: add 1630129, delete 1630129, find 2279/2761
27. Free swap = 0kB
28. Total swap = 2048248kB
29. Out of memory: kill process 2875 (sshd) score 94830592 or a child
30. Killed process 3082 (sshd)
最后出现了Out of memory(内存不足)。这就表示OOM Killer已经运行。无法重新连接的情况就是因为sshd被OOM Killer终止。如果不重新启动sshd就无法登录。
OOM Killer通过终止进程来确保空闲内存,接下来将介绍如何选定这个进程。
进程的选定方法
OOM Killer在内存耗尽时,会查看所有进程,并分别为每个进程计算分数。将信号发送给分数最高的进程。
计算分数的方法
在OOM Killer计算分数时要考虑很多方面。首先要针对每个进程确认下列1~9个事项再计算分数。
1. 首先,计算分数时是以进程的虚拟内存大小为基准的。虚拟内存大小可以使用ps命令的VSZ或/proc/<PID>/status的 VmSize注2来确认。对于正在消耗虚拟内存的进程,其最初的得分较高。单位是将1KB作为1个得分。消耗1GB内存的进程,得分约为1 000 000。
2.如果进程正在执行swapoff系统调用,则得分设置为最大值(unsigned long的最大值)。这是因为禁用swap的行为与消除内存不足是相反的,会立刻将其作为OOM Killer的对象进程。
3.如果是母进程,则将所有子进程内存大小的一半作为分数。
4. 根据进程的CPU使用时间和进程启动时间调整得分。这是因为在这里认为越是长时间运行或从事越多工作的进程越重要,需保持得分较低。
首先,用得分除以CPU使用时间(以10秒为单位)的平方根。如果CPU使用时间为90秒,由于以10秒为单位,因此就是用得分除以9的平方根 “3”。另外,根据进程启动开始的时间也可以调整得分。用得分除以启动时间(以1000秒为单位)的平方根的平方根。如果是持续运行16 000秒的进程,则用得分除以16的平方根“4”的平方根“2”。越是长时间运行的进程就越重要。
小贴士:虽然源代码的备注中写有以10秒为单位、以1000秒为单位,但是实际上在位运算中是以8和1024为单位来计算。
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)不同,如果存在与对象进程共享相同内存空间的进程,则也向这些进程发送信号。
关于OOM Killer的proc文件系统
下面开始介绍与OOM Killer相关的proc文件系统。
1. /proc/<PID>/oom_adj
为/proc/<PID>/oom_adj设置值就可以调整得分。调整值的范围为–16~15。正的值容易被OOM Killer选定。负值可能性较低。例如,当指定3时,得分就变为23倍;当指定–5时,得分就变为1/25。
“–17”是一个特殊的值。如果设置为–17,就会禁止OOM Killer发出的信号(从Linux 2.6.12开始支持设置–17)。
在OOM Killer运行的情况下,为了实现远程登录而想要将sshd排除在对象外时,可以执行下列命令。
1. # cat /proc/'cat /var/run/sshd.pid'/oom_score
2. 15
3. # echo -17 > /proc/'cat /var/run/sshd.pid'/oom_adj
4. # tail /proc/'cat /var/run/sshd.pid'/oom_*
5. ==> /proc/2278/oom_adj <==
6. -17
7. ==> /proc/2278/oom_score <==
8. 0 /*得分变成0*/
从Linux 2.6.18开始可以使用/proc//oom_adj。内容记载在Documentation /filesystems/proc.txt中。
1. /proc/sys/vm/panic_on_oom
将/proc/sys/vm/panic_on_oom设置为1时,在OOM Killer运行时可以不发送进程信号,而是使内核产生重大故障。
1. # echo 1 > /proc/sys/vm/panic_on_oom
2. /proc/sys/vm/oom_kill_allocating_task
从Linux 2.6.24开始proc文件系统就有oom_kill_allocating_task。如果对此设置除0以外的值,则促使OOM Killer运行的进程自身将接收信号。此处省略对所有进程的得分计算过程。
1. # echo 1 > /proc/sys/vm/oom_kill_allocating_task
这样就不需要参照所有进程,但是也不会考虑进程的优先级和root权限等,只发送信号。
1. /proc/sys/vm/oom_dump_tasks
从Linux 2.6.25开始,将oom_dump_tasks设置为除0以外的值时,在OOM Killer运行时的输出中会增加进程的列表信息。
下面为设置示例。
1. # echo 1 > /proc/sys/vm/oom_dump_tasks
列表信息显示如下,可以使用dmesg或syslog来确认。
1. [ pid ] uid tgid total_vm rss cpu oom_adj name
2. [ 1] 0 1 2580 1 0 0 init
3. [ 500] 0 500 3231 0 1 -17 udevd
4. [ 2736] 0 2736 1470 1 0 0 syslogd
5. [ 2741] 0 2741 944 0 0 0 klogd
6. [ 2765] 81 2765 5307 0 0 0 dbus-daemon
7. [ 2861] 0 2861 944 0 0 0 acpid
8. ...
9. [ 3320] 0 3320 525842 241215 1 0 stress
10. /proc/<PID>/oom_score_adj
从Linux 2.6.36开始都安装了/proc/<PID>/oom_score_adj,此后将替换为/proc/ <PID>/oom_adj。详细内容请参考Documentation/feature-removal-schedules.txt。即 使当前是对/proc/<PID>/oom_adj进行的设置,在内核内部进行变换后的值也是针对/proc/<PID> /oom_score_adj设置的。
/proc/<PID>/oom_score_adj可以设置–1000~1000之间的值。设置为–1000时,该进程就被排除在OOM Killer强制终止的对象外。
在内核2.6.36以后的版本中写入oom_adj,只会输出一次如下的信息。
1. # dmesg
2. .....
3. udevd (60): /proc/60/oom_adj is deprecated, please use /proc/60/oom_score_adj instead.
4. .....
RHEL5的特征
在RHEL5中运行OOM Killer时要比在上游内核中更加慎重。OOM Killer会计算调用的次数,仅在一定时间段内超出调用一定次数的情况下运行。
1.OOM Killer从上次调出到下一次调出之间超过5秒时,调用次数重新开始计算。这是为了避免仅因为产生突发性的内存负载就终止进程。
2.在计数变成0后的1秒以内调出时,不计入调用的次数。
3.OOM Killer的调用次数不足10次时,实际不会运行。OOM Killer调用10次时才开始认为内存不足。
4.最后OOM Killer运行不到5秒的话,OOM Killer不会再次运行。因此运行频率最高也有5秒一次。这是为了防止不必要地连续终止多个进程。也有等待接收到OOM Killer发出信号的进程终止(释放内存)的意思。
5. OOM Killer一旦运行,调用的次数就重新回到0。
也就是说,只有在OOM Killer在5秒以内调出的状态连续出现10次以上时才会运行。
这些限制原本是到Linux 2.6.10为止都有的。因此在基于Linux 2.6.9的RHEL4中也需要实施这些限制。当前的上游内核中已经取消了这些限制。
RHEL4的运行
查看OOM Killer在RHEL4(Linux 2.6.9)中的运行情况。在下例中,是内存、交换区都为2GB的环境下,使用负载测试工具stress刻意消耗内存。
stress是给内存、CPU、磁盘I/O施加负载的工具。既可以为其中一项增加负载,也可以同时为这三项中的几项增加负载。stress在运行中如果接收到信号,就会输出信息并终止。
1. # wget -t0 -c http://weather.ou.edu/~apw/projects/stress/stress-1.0.0.tar.gz
2. # tar zxvf stress-1.0.0.tar.gz
3. # cd stress-1.0.0
4. # ./configure ; make ; make install
5. # stress --vm 2 --vm-bytes 2G --vm-keep /* 两个进程分别消耗2GB内存*/
6. stress: info: [17327] dispatching hogs: 0 cpu, 0 io, 2 vm, 0 hdd
7. stress: FAIL: [17327] (416) <-- worker 17328 got signal 15 /* 接收SIGTERM信号*/
8. stress: WARN: [17327] (418) now reaping child worker processes
9. stress: FAIL: [17327] (452) failed run completed in 70s
此时的控制台画面显示如下。
1. oom-killer: gfp_mask=0xd0
2. Mem-info:
3. ...
4. Swap cache: add 524452, delete 524200, find 60/102, race 0+0
5. Free swap: 0kB /* 交换区剩余为0 */
6. 524224 pages of RAM /* 1页4KB,因此内存大小为2GB */
7. 10227 reserved pages /* 在内核内部预约的内存 */
8. 19212 pages shared
9. 253 pages swap cached
10. Out of Memory: Killed process 17328 (stress). /* 根据信号终止的进程 */
在上游内核中无法禁用OOM Killer,而在RHEL4中则通过/proc/sys/vm/oom-kill可以禁用OOM Killer。
1. # echo 0 > /proc/sys/vm/oom-kill
或者
1. # /sbin/sysctl -w vm.oom-kill=0
禁用后OOM Killer就不会发送信号,但是会输出如上内存信息。
RHEL5的运行
在RHEL5(Linux 2.6.18)中对OOM Killer的运行进行确认的方法与RHEL4中相同。
1. # stress --vm 2 --vm-bytes 2G --vm-keep
2. stress: info: [11779] dispatching hogs: 0 cpu, 0 io, 2 vm, 0 hdd
3. stress: FAIL: [11779] (416) <-- worker 11780 got signal 9 /* SIGKILL */
4. stress: WARN: [11779] (418) now reaping child worker processes
5. stress: FAIL: [11779] (452) failed run completed in 46s
此时的控制台画面如下所示。添加了运行OOM Killer时的回溯输出,便于调试。
1. Call Trace:
2. [<ffffffff800bf551>] out_of_memory+0x8e/0x321
3. [<ffffffff8000f08c>] __alloc_pages+0x22b/0x2b4
4. ...
5. [<ffffffff800087fd>] __handle_mm_fault+0x208/0xe04
6. [<ffffffff80065a6a>] do_page_fault+0x4b8/0x81d
7. [<ffffffff800894ad>] default_wake_function+0x0/0xe
8. [<ffffffff80039dda>] tty_ldisc_deref+0x68/0x7b
9. [<ffffffff8005cde9>] error_exit+0x0/0x84
10. Mem-info:
11. ...
12. Swap cache: add 512503, delete 512504, find 90/129, race 0+0
13. Free swap = 0kB
14. Total swap = 2048276kB
15. Free swap: 0kB
16. 524224 pages of RAM
17. 42102 reserved pages
18. 78 pages shared
19. 0 pages swap cached
20. Out of memory: Killed process 11780 (stress).
RHEL6的运行
RHEL6.0中OOM Killer计算得分的方式基本和RHEL5中没有不同。RHEL6系不会如“RHEL5的特征”中所述慎重地运行。其运行基本与上游内核相同。
小结
Linux中的Out Of Memory(OOM) Killer功能作为确保内存的最终手段,可以在耗尽系统内存或交换区后,向进程发送信号,强制终止该进程。
这个功能即使在无法释放内存的情况下,也能够重复进行确保内存的处理过程,防止系统停滞。还可以找出过度消耗内存的进程。本节将介绍2.6内核的OOM Killer。
确认运行、日志
进行系统验证或负载试验时,有时会出现正在运行中的进程终止或者SSH连接突然断开、尝试重新登录也无法连接的情况。
这时需要查看日志。有时会输出如下内核信息。
1. Pid: 4629, comm: stress Not tainted 2.6.26 #3
2. Call Trace:
3. [<ffffffff80265a2c>] oom_kill_process+0x57/0x1dc
4. [<ffffffff80238855>] __capable+0x9/0x1c
5. [<ffffffff80265d39>] badness+0x16a/0x1a9
6. [<ffffffff80265f59>] out_of_memory+0x1e1/0x24b
7. [<ffffffff80268967>] __alloc_pages_internal+0x320/0x3c2
8. [<ffffffff802726cb>] handle_mm_fault+0x225/0x708
9. [<ffffffff8047514b>] do_page_fault+0x3b4/0x76f
10. [<ffffffff80473259>] error_exit+0x0/0x51
11. Node 0 DMA per-cpu:
12. CPU 0: hi: 0, btch: 1 usd: 0
13. CPU 1: hi: 0, btch: 1 usd: 0
14. ...
15. Active:250206 inactive:251609 dirty:0 writeback:0 unstable:0
16. free:3397 slab:2889 mapped:1 pagetables:2544 bounce:0
17. Node 0 DMA free:8024kB min:20kB low:24kB high:28kB active:8kB inactive:180kB present:7448kB pa
18. ges_scanned:308 all_unreclaimable? yes
19. lowmem_reserve[]: 0 2003 2003 2003
20. ...
21. Node 0 DMA: 6*4kB 4*8kB 2*16kB 2*32kB 5*64kB 1*128kB 3*256kB 1*512kB 2*1024kB 2*2048kB 0*4096k
22. B = 8024kB
23. Node 0 DMA32: 1*4kB 13*8kB 1*16kB 6*32kB 2*64kB 2*128kB 1*256kB 1*512kB 0*1024kB 0*2048kB 1*40
24. 96kB = 5564kB
25. 29 total pagecache pages
26. Swap cache: add 1630129, delete 1630129, find 2279/2761
27. Free swap = 0kB
28. Total swap = 2048248kB
29. Out of memory: kill process 2875 (sshd) score 94830592 or a child
30. Killed process 3082 (sshd)
最后出现了Out of memory(内存不足)。这就表示OOM Killer已经运行。无法重新连接的情况就是因为sshd被OOM Killer终止。如果不重新启动sshd就无法登录。
OOM Killer通过终止进程来确保空闲内存,接下来将介绍如何选定这个进程。
进程的选定方法
OOM Killer在内存耗尽时,会查看所有进程,并分别为每个进程计算分数。将信号发送给分数最高的进程。
计算分数的方法
在OOM Killer计算分数时要考虑很多方面。首先要针对每个进程确认下列1~9个事项再计算分数。
1. 首先,计算分数时是以进程的虚拟内存大小为基准的。虚拟内存大小可以使用ps命令的VSZ或/proc/<PID>/status的 VmSize注2来确认。对于正在消耗虚拟内存的进程,其最初的得分较高。单位是将1KB作为1个得分。消耗1GB内存的进程,得分约为1 000 000。
2.如果进程正在执行swapoff系统调用,则得分设置为最大值(unsigned long的最大值)。这是因为禁用swap的行为与消除内存不足是相反的,会立刻将其作为OOM Killer的对象进程。
3.如果是母进程,则将所有子进程内存大小的一半作为分数。
4. 根据进程的CPU使用时间和进程启动时间调整得分。这是因为在这里认为越是长时间运行或从事越多工作的进程越重要,需保持得分较低。
首先,用得分除以CPU使用时间(以10秒为单位)的平方根。如果CPU使用时间为90秒,由于以10秒为单位,因此就是用得分除以9的平方根 “3”。另外,根据进程启动开始的时间也可以调整得分。用得分除以启动时间(以1000秒为单位)的平方根的平方根。如果是持续运行16 000秒的进程,则用得分除以16的平方根“4”的平方根“2”。越是长时间运行的进程就越重要。
小贴士:虽然源代码的备注中写有以10秒为单位、以1000秒为单位,但是实际上在位运算中是以8和1024为单位来计算。
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)不同,如果存在与对象进程共享相同内存空间的进程,则也向这些进程发送信号。
关于OOM Killer的proc文件系统
下面开始介绍与OOM Killer相关的proc文件系统。
1. /proc/<PID>/oom_adj
为/proc/<PID>/oom_adj设置值就可以调整得分。调整值的范围为–16~15。正的值容易被OOM Killer选定。负值可能性较低。例如,当指定3时,得分就变为23倍;当指定–5时,得分就变为1/25。
“–17”是一个特殊的值。如果设置为–17,就会禁止OOM Killer发出的信号(从Linux 2.6.12开始支持设置–17)。
在OOM Killer运行的情况下,为了实现远程登录而想要将sshd排除在对象外时,可以执行下列命令。
1. # cat /proc/'cat /var/run/sshd.pid'/oom_score
2. 15
3. # echo -17 > /proc/'cat /var/run/sshd.pid'/oom_adj
4. # tail /proc/'cat /var/run/sshd.pid'/oom_*
5. ==> /proc/2278/oom_adj <==
6. -17
7. ==> /proc/2278/oom_score <==
8. 0 /*得分变成0*/
从Linux 2.6.18开始可以使用/proc//oom_adj。内容记载在Documentation /filesystems/proc.txt中。
1. /proc/sys/vm/panic_on_oom
将/proc/sys/vm/panic_on_oom设置为1时,在OOM Killer运行时可以不发送进程信号,而是使内核产生重大故障。
1. # echo 1 > /proc/sys/vm/panic_on_oom
2. /proc/sys/vm/oom_kill_allocating_task
从Linux 2.6.24开始proc文件系统就有oom_kill_allocating_task。如果对此设置除0以外的值,则促使OOM Killer运行的进程自身将接收信号。此处省略对所有进程的得分计算过程。
1. # echo 1 > /proc/sys/vm/oom_kill_allocating_task
这样就不需要参照所有进程,但是也不会考虑进程的优先级和root权限等,只发送信号。
1. /proc/sys/vm/oom_dump_tasks
从Linux 2.6.25开始,将oom_dump_tasks设置为除0以外的值时,在OOM Killer运行时的输出中会增加进程的列表信息。
下面为设置示例。
1. # echo 1 > /proc/sys/vm/oom_dump_tasks
列表信息显示如下,可以使用dmesg或syslog来确认。
1. [ pid ] uid tgid total_vm rss cpu oom_adj name
2. [ 1] 0 1 2580 1 0 0 init
3. [ 500] 0 500 3231 0 1 -17 udevd
4. [ 2736] 0 2736 1470 1 0 0 syslogd
5. [ 2741] 0 2741 944 0 0 0 klogd
6. [ 2765] 81 2765 5307 0 0 0 dbus-daemon
7. [ 2861] 0 2861 944 0 0 0 acpid
8. ...
9. [ 3320] 0 3320 525842 241215 1 0 stress
10. /proc/<PID>/oom_score_adj
从Linux 2.6.36开始都安装了/proc/<PID>/oom_score_adj,此后将替换为/proc/ <PID>/oom_adj。详细内容请参考Documentation/feature-removal-schedules.txt。即 使当前是对/proc/<PID>/oom_adj进行的设置,在内核内部进行变换后的值也是针对/proc/<PID> /oom_score_adj设置的。
/proc/<PID>/oom_score_adj可以设置–1000~1000之间的值。设置为–1000时,该进程就被排除在OOM Killer强制终止的对象外。
在内核2.6.36以后的版本中写入oom_adj,只会输出一次如下的信息。
1. # dmesg
2. .....
3. udevd (60): /proc/60/oom_adj is deprecated, please use /proc/60/oom_score_adj instead.
4. .....
RHEL5的特征
在RHEL5中运行OOM Killer时要比在上游内核中更加慎重。OOM Killer会计算调用的次数,仅在一定时间段内超出调用一定次数的情况下运行。
1.OOM Killer从上次调出到下一次调出之间超过5秒时,调用次数重新开始计算。这是为了避免仅因为产生突发性的内存负载就终止进程。
2.在计数变成0后的1秒以内调出时,不计入调用的次数。
3.OOM Killer的调用次数不足10次时,实际不会运行。OOM Killer调用10次时才开始认为内存不足。
4.最后OOM Killer运行不到5秒的话,OOM Killer不会再次运行。因此运行频率最高也有5秒一次。这是为了防止不必要地连续终止多个进程。也有等待接收到OOM Killer发出信号的进程终止(释放内存)的意思。
5. OOM Killer一旦运行,调用的次数就重新回到0。
也就是说,只有在OOM Killer在5秒以内调出的状态连续出现10次以上时才会运行。
这些限制原本是到Linux 2.6.10为止都有的。因此在基于Linux 2.6.9的RHEL4中也需要实施这些限制。当前的上游内核中已经取消了这些限制。
RHEL4的运行
查看OOM Killer在RHEL4(Linux 2.6.9)中的运行情况。在下例中,是内存、交换区都为2GB的环境下,使用负载测试工具stress刻意消耗内存。
stress是给内存、CPU、磁盘I/O施加负载的工具。既可以为其中一项增加负载,也可以同时为这三项中的几项增加负载。stress在运行中如果接收到信号,就会输出信息并终止。
1. # wget -t0 -c http://weather.ou.edu/~apw/projects/stress/stress-1.0.0.tar.gz
2. # tar zxvf stress-1.0.0.tar.gz
3. # cd stress-1.0.0
4. # ./configure ; make ; make install
5. # stress --vm 2 --vm-bytes 2G --vm-keep /* 两个进程分别消耗2GB内存*/
6. stress: info: [17327] dispatching hogs: 0 cpu, 0 io, 2 vm, 0 hdd
7. stress: FAIL: [17327] (416) <-- worker 17328 got signal 15 /* 接收SIGTERM信号*/
8. stress: WARN: [17327] (418) now reaping child worker processes
9. stress: FAIL: [17327] (452) failed run completed in 70s
此时的控制台画面显示如下。
1. oom-killer: gfp_mask=0xd0
2. Mem-info:
3. ...
4. Swap cache: add 524452, delete 524200, find 60/102, race 0+0
5. Free swap: 0kB /* 交换区剩余为0 */
6. 524224 pages of RAM /* 1页4KB,因此内存大小为2GB */
7. 10227 reserved pages /* 在内核内部预约的内存 */
8. 19212 pages shared
9. 253 pages swap cached
10. Out of Memory: Killed process 17328 (stress). /* 根据信号终止的进程 */
在上游内核中无法禁用OOM Killer,而在RHEL4中则通过/proc/sys/vm/oom-kill可以禁用OOM Killer。
1. # echo 0 > /proc/sys/vm/oom-kill
或者
1. # /sbin/sysctl -w vm.oom-kill=0
禁用后OOM Killer就不会发送信号,但是会输出如上内存信息。
RHEL5的运行
在RHEL5(Linux 2.6.18)中对OOM Killer的运行进行确认的方法与RHEL4中相同。
1. # stress --vm 2 --vm-bytes 2G --vm-keep
2. stress: info: [11779] dispatching hogs: 0 cpu, 0 io, 2 vm, 0 hdd
3. stress: FAIL: [11779] (416) <-- worker 11780 got signal 9 /* SIGKILL */
4. stress: WARN: [11779] (418) now reaping child worker processes
5. stress: FAIL: [11779] (452) failed run completed in 46s
此时的控制台画面如下所示。添加了运行OOM Killer时的回溯输出,便于调试。
1. Call Trace:
2. [<ffffffff800bf551>] out_of_memory+0x8e/0x321
3. [<ffffffff8000f08c>] __alloc_pages+0x22b/0x2b4
4. ...
5. [<ffffffff800087fd>] __handle_mm_fault+0x208/0xe04
6. [<ffffffff80065a6a>] do_page_fault+0x4b8/0x81d
7. [<ffffffff800894ad>] default_wake_function+0x0/0xe
8. [<ffffffff80039dda>] tty_ldisc_deref+0x68/0x7b
9. [<ffffffff8005cde9>] error_exit+0x0/0x84
10. Mem-info:
11. ...
12. Swap cache: add 512503, delete 512504, find 90/129, race 0+0
13. Free swap = 0kB
14. Total swap = 2048276kB
15. Free swap: 0kB
16. 524224 pages of RAM
17. 42102 reserved pages
18. 78 pages shared
19. 0 pages swap cached
20. Out of memory: Killed process 11780 (stress).
RHEL6的运行
RHEL6.0中OOM Killer计算得分的方式基本和RHEL5中没有不同。RHEL6系不会如“RHEL5的特征”中所述慎重地运行。其运行基本与上游内核相同。
小结
本节介绍了OOM Killer的结构和各种设置。当系统运行异常时确认syslog等,如果有OOM Killer的输出,就可以得知曾出现内存不足。
修改分数
sprintf(text, "/proc/%d/oom_score_adj", getpid());
int fd = open(text, O_WRONLY);
if (fd >= 0)
{
//printf("text = %s\n", text);
sprintf(text, "%d", -1000);
write(fd, text, strlen(text));
//printf("write text = %s\n", text);
close(fd);
}