0%

Linux下malloc函数和OOM Killer

Malloc函数和OOM Killer

Linux下malloc函数主要用来在用户空间从heap申请内存,申请成功返回指向所分配内存的指针,申请失败返回NULL。
默认情况下,Linux内核使用“乐观的”分配内存策略,首先粗略估计系统可使用的内存数,然后分配内存,
但是在使用的时候才真正把这块分配的内存给你。
这样一来,即使用malloc申请内存没有返回NULL,你也不一定能完全使用这块内存,
特别是在一次或连续多次申请很多内存的时候。

如果一直连续用malloc申请内存,而不真正使用,所申请的内存总数可以超过真正可以使用的内存数。
但是当真正使用这块内存,比如用memset或bzero函数一次性把所申请到的大块内存“使用掉”,
Linux系统就会Out Of Memory,这个时候OOM Killer就会kill掉用户空间的其他进程来腾出更多可使用内存。

OOM Killer根据OOM score来决定kill哪个进程,OOM score可以看/proc//oom_score,
score由badness函数计算得出,根据进程运行时间长短,进程优先级,进程所使用的内存数等等。
可以通过/proc//oom_adj来干预计算socre,这个值的取值范围是-17~15,
如果是-17该进程就永远不会被kill(这个可能也和内核版本有关,不见得所有内核版本都支持,得实际试试)。

“默认情况”Linux是这种做的,“默认情况”是指/proc/sys/vm/overcommit_memory为0的时候。
这个参数也可以调整,如果为1表示“来着不拒”,只要你malloc过来申请,我啥都不做,立马给你分配内存,
这样的话性能就会有大幅度的提高;
如果为2表示Linux会精确计算所有可使用的内存和所申请的内存,如果所申请的超过的可使用的内存数就返回NULL。
可使用的内存值计算方法,虚拟内存(swap)+ /proc/sys/vm/overcommit_memory(百分比) × 物理内存。
/proc/sys/vm/overcommit_memory默认值为50,计算起来就是50%的物理内存数。

Linux自身内核会占一部分内存,还有buffer/cache所占用的内存,
所以实际上能被malloc申请后使用的内存并非物理内存大小,
demsg的输出里面包含了相关信息(如果看不到,可能是被别的信息冲掉了,重启系统,在系统起来后马上看):

Memory: 2071220k/2097152k available (2122k kernel code, 24584k reserved, 884k data, 228k init, 1179584k highmem)

关于OOM Killer的proc文件系统

下面开始介绍与OOM Killer相关的proc文件系统。

/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排除在对象外时,可以执行下列命令。

# cat /proc/'cat /var/run/sshd.pid'/oom_score
15
# echo -17 >  /proc/'cat /var/run/sshd.pid'/oom_adj
# tail /proc/'cat /var/run/sshd.pid'/oom_*
==> /proc/2278/oom_adj <==
-17
==> /proc/2278/oom_score <==
0                               /*得分变成0*/

从Linux 2.6.18开始可以使用/proc/PID/oom_adj。内容记载在Documentation /filesystems/proc.txt中。

/proc/sys/vm/panic_on_oom

将/proc/sys/vm/panic_on_oom设置为1时,在OOM Killer运行时可以不发送进程信号,而是使内核产生重大故障。

# echo 1 > /proc/sys/vm/panic_on_oom

/proc/sys/vm/oom_kill_allocating_task

从Linux 2.6.24开始proc文件系统就有oom_kill_allocating_task。
如果对此设置除0以外的值,则促使OOM Killer运行的进程自身将接收信号。此处省略对所有进程的得分计算过程。

# echo 1 > /proc/sys/vm/oom_kill_allocating_task

这样就不需要参照所有进程,但是也不会考虑进程的优先级和root权限等,只发送信号。

/proc/sys/vm/oom_dump_tasks

从Linux 2.6.25开始,将oom_dump_tasks设置为除0以外的值时,在OOM Killer运行时的输出中会增加进程的列表信息。

# echo 1 > /proc/sys/vm/oom_dump_tasks

列表信息显示如下,可以使用dmesg或syslog来确认。

[ pid ]   uid  tgid total_vm      rss cpu oom_adj name
[    1]     0     1     2580        1   0       0 init
[  500]     0   500     3231        0   1     -17 udevd
[ 2736]     0  2736     1470        1   0       0 syslogd
[ 2741]     0  2741      944        0   0       0 klogd
[ 2765]    81  2765     5307        0   0       0 dbus-daemon
[ 2861]     0  2861      944        0   0       0 acpid
...
[ 3320]     0  3320   525842   241215   1       0 stress

/proc/PID/oom_score_adj

从Linux 2.6.36开始都安装了/proc//oom_score_adj,
此后将替换为/proc/PID/oom_adj。
即使当前是对/proc/PID/oom_adj进行的设置,在内核内部进行变换后的值也是针对/proc/PID/oom_score_adj设置的。

可以设置–1000~1000之间的值。设置为–1000时,该进程就被排除在OOM Killer强制终止的对象外。

在内核2.6.36以后的版本中写入oom_adj,只会输出一次如下的信息。

# dmesg
.....
udevd (60): /proc/60/oom_adj is deprecated, please use /proc/60/oom_score_adj instead.