Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1492003
  • 博文数量: 263
  • 博客积分: 10851
  • 博客等级: 上将
  • 技术积分: 2627
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-26 22:40
文章分类

全部博文(263)

文章存档

2013年(4)

2012年(25)

2011年(33)

2010年(50)

2009年(138)

2008年(13)

分类: LINUX

2009-11-18 18:07:13

统计组的同事发现的一个问题,  一个统计用脚本在运行到中途的就被系统自动kill了, 搜了下, 可能跟linux本身的oom killer机制有关系:
 
第一次注意到 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
更多参考信息:
    * 1) OOM killer "Out of Memory: Killed process" SOLUTIONS / SUMMARY【对我遇到的案例没鸟用】
    * 2) Metalink Notes : Linux Kernel Lowmem Pressure Issues and Kernel Structures
    * 3) Respite from the OOM killer

--EOF--
 
 
 
apache 终于停止服务了,系统为2.6内核、64位操作系统、2G内存。
/var/log/messages 有这样的错误提示
Mar  8 20:18:24 2hei-net kernel: oom-killer: gfp_mask=0x1d2
Mar  8 20:18:24 2hei-net kernel: Mem-info:
Mar  8 20:18:24 2hei-net kernel: Node 0 DMA per-cpu:
Mar  8 20:18:24 2hei-net kernel: cpu 0 hot: low 2, high 6, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 0 cold: low 0, high 2, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 1 hot: low 2, high 6, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 1 cold: low 0, high 2, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 2 hot: low 2, high 6, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 2 cold: low 0, high 2, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 3 hot: low 2, high 6, batch 1
Mar  8 20:18:24 2hei-net kernel: cpu 3 cold: low 0, high 2, batch 1
Mar  8 20:18:24 2hei-net kernel: Node 0 Normal per-cpu:
Mar  8 20:18:26 2hei-net kernel: cpu 0 hot: low 32, high 96, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 0 cold: low 0, high 32, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 1 hot: low 32, high 96, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 1 cold: low 0, high 32, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 2 hot: low 32, high 96, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 2 cold: low 0, high 32, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 3 hot: low 32, high 96, batch 16
Mar  8 20:18:26 2hei-net kernel: cpu 3 cold: low 0, high 32, batch 16
Mar  8 20:18:26 2hei-net kernel: Node 0 HighMem per-cpu: empty
Mar  8 20:18:26 2hei-net kernel:
Mar  8 20:18:26 2hei-net kernel: Free pages:       17536kB (0kB HighMem)
Mar  8 20:18:26 2hei-net kernel: Active:257583 inactive:239023 dirty:0 writeback:0 unstable:0 free:4384 slab:3787 mapped:497010 pagetables:2846
Mar  8 20:18:26 2hei-net kernel: Node 0 DMA free:11832kB min:44kB low:88kB high:132kB active:0kB inactive:0kB present:16384kB pages_scanned:0 all_unreclaimable? yes
Mar  8 20:18:26 2hei-net kernel: protections[]: 0 0 0
Mar  8 20:18:26 2hei-net kernel: Node 0 Normal free:5704kB min:5720kB low:11440kB high:17160kB active:1029692kB inactive:956732kB present:2080416kB pages_scanned:3188856 all_unreclaimable? yes
Mar  8 20:18:26 2hei-net kernel: protections[]: 0 0 0
Mar  8 20:18:26 2hei-net kernel: Node 0 HighMem free:0kB min:128kB low:256kB high:384kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
Mar  8 20:18:26 2hei-net kernel: protections[]: 0 0 0
Mar  8 20:18:26 2hei-net kernel: Node 0 DMA: 6*4kB 4*8kB 2*16kB 3*32kB 2*64kB 2*128kB 2*256kB 1*512kB 0*1024kB 1*2048kB 2*4096kB = 11832kB
Mar  8 20:18:26 2hei-net kernel: Node 0 Normal: 0*4kB 1*8kB 4*16kB 0*32kB 0*64kB 0*128kB 0*256kB 1*512kB 1*1024kB 0*2048kB 1*4096kB = 5704kB
Mar  8 20:18:26 2hei-net kernel: Node 0 HighMem: empty
Mar  8 20:18:26 2hei-net kernel: Swap cache: add 1070476, delete 1070476, find 119872/179715, race 0+20
Mar  8 20:18:26 2hei-net kernel: Free swap:            0kB
Mar  8 20:18:26 2hei-net kernel: 524200 pages of RAM
Mar  8 20:18:27 2hei-net kernel: 10214 reserved pages
Mar  8 20:18:27 2hei-net kernel: 67015 pages shared
Mar  8 20:18:27 2hei-net kernel: 0 pages swap cached
Mar  8 20:18:27 2hei-net kernel: Out of Memory: Killed process 26079 (httpd).
终于发现了linux的OOM Killer(Out of Memory: Killed process)这个功能。当linux发现有进程占用内存过多时会触发OOM Killer功能,将占用内存最多的pid给杀掉,通过网上的一些惨痛的教训可以看到有mysql、oracle、apache给OOM kill掉的。
因为我的机器上只跑了apache服务,所以又看了一下我的apache配置

StartServers         2
MaxClients         2048
MinSpareThreads     25
MaxSpareThreads     75
ThreadsPerChild     128
MaxRequests
本以为想让apache支持的连接数多一点,没想到MaxClients参数的设置影响了系统的稳定。
我看到以这种配置启动时VIRT  RES的值就已经显示为1702,RES从11m开始逐步升高。
 PID USER      PR  NI %CPU    TIME+  %MEM  VIRT  RES  SHR S COMMAND                                                               
 7009 apache    16   0    1   0:06.95  0.8  1702m  11m 2172 S /home/local/apache/bin/httpd                           
跑了几天以后VIRT已经到了2G多,RES也接近1G,终于今天挂掉了,httpd进程被linux给kill掉了。
于是尝试修改apache的httd.conf配置,发现MaxClients为1536时,启动VIRT可以达到1024m,如果设定为1024时 VIRT为776m,所以对于2G内存的机器不要超过1536为好。
参考文档:
 
摘自:
 
 
 
015 mm/oom_kill.c
2006-5-26
mm/oom_kill.c
*
* 忙,并且忙了很久,占有的少,和权利大的靠边,并毫不谦让,直接出手的有更
* 多生还机会
*
 
 超级纯粹的一个模块,实现out of memory killer.当内存严重不足的时候选择
一个"弱者",同过强制信号kill掉,释放出内存。
 提供了两个借口:
 void oom_kill(void)
 int out_of_memory(void)
 并且只有一个地方使用,vmscan.c:
int kswapd(void *unused)
{
 ........
 else if (out_of_memory()) {
 oom_kill();
 }
 
}
 分析filemap.c的时候,了解过kswapd的作用,罗列一下:
*****kswapd (mm/vmscan.c)
 +---->do_try_to_free_pages (如果内存已经不够用)
 +-->page_launder
 | +-->扫描
 | +-->对dirty页启动回写(包括mapping和buffer cache)
 +-->refill_inactive
 +-->refill_inactive_scan
 +-->扫描,选择合适页面移入
 
 +-->swap_out,对进程启动页面换出
 +-->try_to_swap_out将选中页面放入
 
 +----->refill_inactive_scan
 
 
 先来看看什么叫做内存严重不足:
int out_of_memory(void)
{
 struct sysinfo swp_info;
 /* Enough free memory? Not OOM. */
 if (nr_free_pages() > freepages.min)//每个node,每个zone的buddy系统页面余量不足
 return 0;
 if (nr_free_pages() + nr_inactive_clean_pages() > freepages.low)//即使算上可以立即
 return 0; //回收的页面,也少的可怜
 /* Enough swap space left? Not OOM. */
 si_swapinfo(&swp_info);
 if (swp_info.freeswap > 0)//连磁盘上的空间都不够用了
 return 0;
 /* Else... */
 return 1; //请找个替死鬼吧
}
 然后就是选择哪个进程的问题了:
void oom_kill(void)
{
 //选择一个进程
 struct task_struct *p = select_bad_process();
 ...
 //授予选中的进程很高的优先级,让他尽快结束运行
 p->counter = 5 * HZ;
 p->flags |= PF_MEMALLOC;
 /* This process has hardware access, be more careful. */
 //强制发送信号,结束其运行
 if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO)) {
 force_sig(SIGTERM, p);
 } else {
 force_sig(SIGKILL, p);
 }
 /*
 * Make kswapd go out of the way, so "p" has a good chance of
 * killing itself before someone else gets the chance to ask
 * for more memory.
 */
 //只有kswapd调用此函数,所以,这就是让kswapd让出cpu
 //重新调度的时候,被选中的进程可可能尽快结束运行,释放资源
 current->policy |= SCHED_YIELD;
 schedule();
 return;
}
 
 kswapd(oom_kill)选中要kill的进程,发送一个信号给这个进程,然后kswapd让
出cpu,是选中的进程可以得到调度.当选中的进程开始运行的时候,因为有一个信号
处于peding状态,所以系统建立一个运行signal的环境,并开始处理信号.最后被选
中的进程退出运行,释放资源.至于更详细的过程等到分析信号的时候再议.
 
 select_bad_process选择badness值最高的进程,我们来看看什么样的进程最有
可能为系统牺牲:
static int badness(struct task_struct *p)
{
 int points, cpu_time, run_time;
 if (!p->mm)
 return 0;
 /*
 * The memory size of the process is the basis for the badness.
 */
 points = p->mm->total_vm; //占有内存越多,越可能被干掉
 /*
 * CPU time is in seconds and run time is in minutes. There is no
 * particular reason for this other than that it turned out to work
 * very well in practice. This is not safe against jiffie wraps
 * but we don't care _that_ much...
 */
 cpu_time = (p->times.tms_utime + p->times.tms_stime) >> (SHIFT_HZ + 3);
 run_time = (jiffies - p->start_time) >> (SHIFT_HZ + 10);
 points /= int_sqrt(cpu_time);//占有cpu越多,即越忙,就越有可能生存
 points /= int_sqrt(int_sqrt(run_time));//运行时间越久越有生存机会
 /*
 * Niced processes are most likely less important, so double
 * their badness points.
 */
 if (p->nice > 0)
 points *= 2;//越谦让就死的越快(不要说不公平啊)
 /*
 * Superuser processes are usually more important, so we make it
 * less likely that we kill those.
 */
 //管理员的程序需要尽力保留下来
 if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
 p->uid == 0 || p->euid == 0)
 points /= 4;
 /*
 * We don't want to kill a process with direct hardware access.
 * Not only could that mess up the hardware, but usually users
 * tend to only have this flag set on applications they think
 * of as important.
 */
 //那些直接操作硬件的程序可以获取更多生存机会
 if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
 points /= 4;
#ifdef DEBUG
 printk(KERN_DEBUG "OOMkill: task %d (%s) got %d points\n",
 p->pid, p->comm, points);
#endif
 return points;
}
阅读(1455) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~