Chinaunix首页 | 论坛 | 博客
  • 博客访问: 438805
  • 博文数量: 161
  • 博客积分: 5005
  • 博客等级: 上校
  • 技术积分: 1090
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-20 16:38
文章分类

全部博文(161)

文章存档

2011年(21)

2010年(33)

2009年(89)

2008年(18)

我的朋友

分类: LINUX

2011-02-10 00:19:08

一)swap的概述

1)swap的作用可简单描述为:
当内存不够用时,将存储器中的数据块从DRAM移到swap的磁盘空间中,以释放更多的空间给当前进程使用.
当再次需要那些数据时,就可以将swap磁盘中的数据重新移到内存,而将那些不用的数据块从内存移到swap中.

2)数据从内存移动交换区的行为被称为页面调用,发生在后台的页面调用没有来自应用程序的干涉.

3)swap空间是分页的,每一页的大小和内存页的大小一样.

4)并不是一定要给每个系统划分SWAP,比如大多数的嵌入式就没有swap.

 

二)swap能让系统最多使用多少内存?

一个32位的LINUX系统在没有启用PAE的情况下最多可以用4GB的物理内存.在用户空间中最多可用3GB.
而增加swap会让系统增加可使用的内存空间吗?

用下面的测试回答这个问题:

查看内核版本
[root@test1 tmp]# uname -r
2.6.18-8.el5
建一个10GB的文件
[root@test1 tmp]# dd if=/dev/zero f=data.dat bs=10M count=1000

[root@test1 tmp]# mkswap data.dat 
Setting up swapspace version 1, size = 10485755 kB

[root@test1 tmp]# free -m
             total       used       free     shared    buffers     cached
Mem:           503         43        460          0         13          9
-/+ buffers/cache:         20        482
Swap:         1027         22       1005

[root@test1 tmp]# swapon data.dat

[root@test1 tmp]# free -m
             total       used       free     shared    buffers     cached
Mem:           503         46        457          0         15          9
-/+ buffers/cache:         20        482
Swap:        11027         22      11005


[root@test1 tmp]# swapon -s 
Filename                                Type            Size    Used    Priority
/dev/sda2                               partition       1052248 23040   -1
/tmp/data.dat                           file            10239992        0       -3


现在的物理内存和swap加起来超过了4GB.而我们最多能用多少呢?


下面的程序会不断申请/分配内存空间.
源程序如下:test1
#include
#include
#include

int main (int argc, char *argv[])
{
        void *ptr;
        int n = 0;
        while (1){
                ptr = malloc(0x100000);

                if (ptr == NULL)
                        break;

                memset(ptr, 1, 0x100000);
                printf("malloced %d MB\n", ++n);
        }

        pause();
}

[root@test1 tmp]# gcc test1.c -o callmem
[root@test1 tmp]# ./callmem


malloced 3039 MB
malloced 3040 MB
malloced 3041 MB
malloced 3042 MB
malloced 3043 MB
malloced 3044 MB
malloced 3045 MB
malloced 3046 MB
malloced 3047 MB
malloced 3048 MB
malloced 3049 MB
malloced 3050 MB
malloced 3051 MB
malloced 3052 MB
malloced 3053 MB
malloced 3054 MB
malloced 3055 MB
malloced 3056 MB

在分配了3056MB的内存之后,再不能分配了,说明即使通过swap给系统增加了10GB的内存,系统最终也只能使用3056MB的内存空间(用户空间),不能突破4GB的限制.


三)超过了系统最大可使用内存空间会怎样?

依旧是上面的例子,我们关闭掉swap文件data.dat

[root@test1 tmp]# swapoff data.dat

查看当前的swap空间
[root@test1 tmp]# swapon -s 
Filename                                Type            Size    Used    Priority
/dev/sda2                               partition       1052248 22760   -1

执行callmem
[root@test1 tmp]# ./callmem

malloced 1465 MB
malloced 1466 MB
malloced 1467 MB
malloced 1468 MB
malloced 1469 MB
malloced 1470 MB
malloced 1471 MB
malloced 1472 MB
malloced 1473 MB
malloced 1474 MB
malloced 1475 MB
malloced 1476 MB
malloced 1477 MB
malloced 1478 MB
malloced 1479 MB
malloced 1480 MB
malloced 1481 MB
malloced 1482 MB
malloced 1483 MB
malloced 1484 MB
malloced 1485 MB
Killed

在分配了1485MB内存后,程序退出.

在/var/log/message中查看报错的信息,如下:

May 30 10:40:52 test1 kernel: automount invoked oom-killer: gfp_mask=0x201d2, rder=0, mkilladj=0
May 30 10:40:52 test1 kernel:  [] out_of_memory+0x3b/0x179
May 30 10:40:52 test1 kernel:  [] __alloc_pages+0x1fe/0x27e
May 30 10:40:52 test1 kernel:  [] __do_page_cache_readahead+0xc4/0x1c6
May 30 10:40:52 test1 kernel:  [] sync_page+0x0/0x3b
May 30 10:40:52 test1 kernel:  [] __delayacct_blkio_end+0x32/0x35
May 30 10:40:52 test1 kernel:  [] __wait_on_bit_lock+0x4b/0x52
May 30 10:40:52 test1 kernel:  [] __lock_page+0x51/0x57
May 30 10:40:52 test1 kernel:  [] filemap_nopage+0x151/0x315
May 30 10:40:52 test1 kernel:  [] __handle_mm_fault+0x172/0x87b
May 30 10:40:52 test1 kernel:  [] __activate_task+0x1c/0x29
May 30 10:40:52 test1 kernel:  [] wake_up_new_task+0x1be/0x1c6
May 30 10:40:52 test1 kernel:  [] do_page_fault+0x20a/0x4b8
May 30 10:40:52 test1 kernel:  [] do_page_fault+0x0/0x4b8
May 30 10:40:52 test1 kernel:  [] error_code+0x39/0x40
May 30 10:40:52 test1 kernel:  =======================
May 30 10:40:52 test1 kernel: Mem-info:
May 30 10:40:52 test1 kernel: DMA per-cpu:
May 30 10:40:52 test1 kernel: cpu 0 hot: high 0, batch 1 used:0
May 30 10:40:52 test1 kernel: cpu 0 cold: high 0, batch 1 used:0
May 30 10:40:52 test1 kernel: DMA32 per-cpu: empty
May 30 10:40:52 test1 kernel: Normal per-cpu:
May 30 10:40:52 test1 kernel: cpu 0 hot: high 186, batch 31 used:30
May 30 10:40:52 test1 kernel: cpu 0 cold: high 62, batch 15 used:14
May 30 10:40:52 test1 kernel: HighMem per-cpu: empty
May 30 10:40:52 test1 kernel: Free pages:        4844kB (0kB HighMem)
May 30 10:40:52 test1 kernel: Active:62495 inactive:62136 dirty:0 writeback:4 unstable:0 free:1211 slab:1350 mapped:0 pagetables:693
May 30 10:40:52 test1 kernel: DMA free:2072kB min:88kB low:108kB high:132kB active:5108kB inactive:5084kB present:16384kB pages_scan
ned:15907 all_unreclaimable? yes
May 30 10:40:52 test1 kernel: lowmem_reserve[]: 0 0 496 496
May 30 10:40:52 test1 kernel: DMA32 free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unrecl
aimable? no
May 30 10:40:52 test1 kernel: lowmem_reserve[]: 0 0 496 496
May 30 10:40:52 test1 kernel: Normal free:2772kB min:2804kB low:3504kB high:4204kB active:244872kB inactive:243460kB present:507904k
B pages_scanned:990188 all_unreclaimable? yes
May 30 10:40:52 test1 kernel: lowmem_reserve[]: 0 0 0 0
May 30 10:40:52 test1 kernel: HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scanned:0 al
l_unreclaimable? no
May 30 10:40:52 test1 kernel: lowmem_reserve[]: 0 0 0 0
May 30 10:40:52 test1 kernel: DMA: 0*4kB 1*8kB 1*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 2072kB
May 30 10:40:52 test1 kernel: DMA32: empty
May 30 10:40:52 test1 kernel: Normal: 1*4kB 0*8kB 3*16kB 1*32kB 0*64kB 1*128kB 0*256kB 1*512kB 0*1024kB 1*2048kB 0*4096kB = 2772kB
May 30 10:40:52 test1 kernel: HighMem: empty
May 30 10:40:52 test1 kernel: Swap cache: add 2790571, delete 2790571, find 1739/3390, race 2+1
May 30 10:40:53 test1 kernel: Free swap  = 0kB
May 30 10:40:53 test1 kernel: Total swap = 1052248kB
May 30 10:40:53 test1 kernel: Free swap:            0kB
May 30 10:40:53 test1 kernel: 131072 pages of RAM
May 30 10:40:53 test1 kernel: 0 pages of HIGHMEM
May 30 10:40:53 test1 kernel: 2188 reserved pages
May 30 10:40:53 test1 kernel: 59 pages shared
May 30 10:40:53 test1 kernel: 0 pages swap cached
May 30 10:40:53 test1 kernel: 0 pages dirty
May 30 10:40:53 test1 kernel: 4 pages writeback
May 30 10:40:53 test1 kernel: 0 pages mapped
May 30 10:40:53 test1 kernel: 1350 pages slab
May 30 10:40:53 test1 kernel: 693 pages pagetables
May 30 10:40:53 test1 kernel: Out of memory: Killed process 2720 (callmem)


通过测试证明在程序申请了多于系统的内存可使用空间时,程序将被中止.同时系统将回收这个程序已申请分配的内存空间.

 


四)关于内存耗尽的总结:

1)在进程收到OOM之前,内核将刷新文件系统的cache来释放空间.
2)将交换区的页面移到磁盘上.
3)当内存变少时,虚拟性使每个进程通过交换区来做简单的上下文环境切换.
4)当进程消耗尽交换内存后,才会引发out-of-memory(OOM)来kill那些进程.

 

五)内存耗尽的解决办法

有三种方法解决进程对内存的疯狂占用.

1)overcommit

当系统用完RAM和交换区后,内核将终止响应进程,malloc持续地返回远大于系统所能提供的指向虚拟地址的指针,Linux称这种情况为overcommit.
overcommit默认是0,处于开启状态,允许一个进程分配多于当前可用的内存,而进程耗尽可用内存时,将被OOM(out-of-memroy)终止.
可以人为将overcommit改为2,即关闭covercommit,它迫使内核在分配内存时,是基于当前可用的物理内存.

通过下面的程序测试malloc和overcommit,这个程序与之前的程序最大不同是,程序只分配内存(malloc),但不通过memset来修改它申请的数据,这样就不会产生缺页.

源程序如下:test2
#include
#include
#include

int
main (int argc, char *argv[])
{
        void *ptr;
        int n = 0;
        while (1){
                ptr = malloc(0x100000);
                if (ptr == NULL)
                        break;
                n++;
        }

        printf("malloced %d MB\n", n);
        pause();
}

现在overcommit处于开启状态:
[root@test1 ~]# cat /proc/sys/vm/overcommit_memory 
0

为便于测试将swap分区内存关闭:
[root@test1 tmp]# swapoff -a

编译程序test2.c
[root@test1 tmp]# gcc test2.c -o callmem1

执行callmem1,发现它申请了3056MB,而物理内存只有512MB.
[root@test1 tmp]# ./callmem1 
malloced 3056 MB

现在关闭overcommit
[root@test1 tmp]# echo 2 > /proc/sys/vm/overcommit_memory

[root@test1 tmp]# free
             total       used       free     shared    buffers     cached
Mem:        515600      49624     465976          0       1464      14908
-/+ buffers/cache:      33252     482348
Swap:            0          0          0

再次执行callmem1,发现它这回只申请到了182MB的内存.
[root@test1 tmp]# ./callmem1 
malloced 182 MB


malloc只会分配内存,它没有修改数据,所以并不用占用内存空间.通过free命令查看后没有明显变化.
[root@test1 tmp]# free
             total       used       free     shared    buffers     cached
Mem:        515600      50244     465356          0       1472      14908
-/+ buffers/cache:      33864     481736
Swap:            0          0          0


2)ulimit

通过ulimit系统命令限制当前shell只能用10MB的内存资源.
[root@test1 tmp]# ulimit -d 10240 -m 10240 -v 10240

-d     The maximum size of a process data segment
-m     The maximum resident set size
-v     The maximum amount of virtual memory available to the shell

其中-v是决定性的,它决定了当前shell可用的最多虚拟内存.

检查设置的结果
[root@test1 tmp]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) 10240
max nice                        (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 8192
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) 10240
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
max rt priority                 (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 8192
virtual memory          (kbytes, -v) 10240
file locks                      (-x) unlimited

最后执行callmem1
[root@test1 tmp]# ./callmem1&
[1] 2525
malloced 8 MB
我们限制到了10MB,为什么只能分配8MB的内存.

用pmap查看映射地址.
[root@test1 tmp]# jobs -x pmap %1
2525:   ./callmem1
00110000   1244K r-x--  /lib/libc-2.5.so
00247000      8K r-x--  /lib/libc-2.5.so
00249000      4K rwx--  /lib/libc-2.5.so
0024a000     12K rwx--    [ anon ]
00b51000    100K r-x--  /lib/ld-2.5.so
00b6a000      4K r-x--  /lib/ld-2.5.so
00b6b000      4K rwx--  /lib/ld-2.5.so
00c68000      4K r-x--    [ anon ]
08048000      4K r-x--  /tmp/callmem1
08049000      4K rw---  /tmp/callmem1
b7777000   8228K rw---    [ anon ]
b7f93000      8K rw---    [ anon ]
bfe56000     88K rw---    [ stack ]
 total     9712K

我们看到除了程序自身malloc分配掉的内存之外,还分别加载了动态链接库libc-2.5.so,ld-2.5.so.
在通过malloc分配了8228KB时,程序已经不能再申请1MB的内存了.


3)setrlimit

我们在实际应用的过程式中,可以通过sysconf(_SC_AVPHYS_PAGES);获取系统中可用物理内存页的数量.
而不包括刷新的cache和交换到磁盘上的空间.它和MemFree的值很相近.可以通过/proc/meminfo查看.
这个值应该说是一个很保存的值,因为它不包括通过刷新为人所知cache而释放的空间.

我们通过setrlimit系统调用,来保证当前进程只能使用sysconf(_SC_AVPHYS_PAGES)获得保守值.

第一步去掉上一个试验留下的内存限制.
[root@test1 tmp]# ulimit -d unlimited -m unlimited -v unlimited

编译test3.c
[root@test1 tmp]# gcc test3.c -o callmem2

源程序如下:test3.c
#include
#include
#include
#include
#include
#include
#include
#include
#include

int
main (int argc, char *argv[])
{
        void *ptr;
        int n = 0;
        int r = 0;
        struct rlimit rl;
        u_long pages, max_pages, max_bytes;
        pages = sysconf(_SC_AVPHYS_PAGES);

        max_pages = ULONG_MAX / sysconf(_SC_PAGE_SIZE);
        if (pages > max_pages)
                pages = max_pages;
        max_bytes = pages * sysconf(_SC_PAGE_SIZE);

        r = getrlimit (RLIMIT_AS,&rl);

        printf("crrent hard limit is %ld MB\n",
                (u_long) rl.rlim_max/0x100000);

        rl.rlim_cur = max_bytes;
        r = setrlimit (RLIMIT_AS, &rl);
        if (r){
                perror("setrlimit");
        }

        printf("limit set to %ld MB\n", max_bytes / 0x100000);

        while(1){
                ptr = malloc(0x100000);

                if (ptr == NULL){
                        perror("malloc");
                        break;
                }
                memset(ptr, 1, 0x100000);
                printf("malloced %d MB\n", ++n);
        }
        printf("paused\n");
        raise(SIGSTOP);
        return 0;
}

先查看一下当前内存利用情况,当前空闲内存为296MB.
[root@test1 ~]# free -m 
             total       used       free     shared    buffers     cached
Mem:           503        206        296          0         28        141
-/+ buffers/cache:         36        466
Swap:         1027          0       1027

执行callmem2程序
[root@test1 tmp]# ./callmem2 
crrent hard limit is 4095 MB
limit set to 296 MB
malloced 1 MB
malloced 2 MB
malloced 3 MB
malloced 4 MB
malloced 5 MB
......
malloced 294 MB
malloc: Cannot allocate memory
paused

[1]+  Stopped                 ./callmem2
crrent hard limit is 4095 MB
当前系统的内存限额为4095MB,也就是4GB的虚拟内存限制.
limit set to 296 MB
而进程最多也只能用到296MB,最后通过malloc分配到了294MB,还有2MB给了系统库.

再看一下当前的内存:
[root@test1 ~]# free -m 
             total       used       free     shared    buffers     cached
Mem:           503        497          5          0         28        137
-/+ buffers/cache:        331        171
Swap:         1027          0       1027

这里为什么还剩下5MB的内存呢,其实它把内存都给了callmem2(剩下的零头没法再给了).它是将cached中的4MB内存释放给了系统.


那么在这种情况下,别的进程还能分配到内存吗?

答案是可以的,但最多只能是分配5MB左右.比如上面free的内存有5MB,进程启动的时候会分配1MB-2MB的内存给系统库.剩下的它会尽可能的占用.
再次执行callmem2,进程只分配到了4MB.
[root@test1 tmp]# ./callmem2 
crrent hard limit is 4095 MB
limit set to 6 MB
malloced 1 MB
malloced 2 MB
malloced 3 MB
malloced 4 MB
malloc: Cannot allocate memory
paused

[2]+  Stopped                 ./callmem2


用pmap查看映射的地址,最多用了5728KB.
[root@test1 tmp]# jobs -x pmap %2
2739:   ./callmem2
00110000   1244K r-x--  /lib/libc-2.5.so
00247000      8K r-x--  /lib/libc-2.5.so
00249000      4K rwx--  /lib/libc-2.5.so
0024a000     12K rwx--    [ anon ]
00b51000    100K r-x--  /lib/ld-2.5.so
00b6a000      4K r-x--  /lib/ld-2.5.so
00b6b000      4K rwx--  /lib/ld-2.5.so
00ba3000      4K r-x--    [ anon ]
08048000      4K r-x--  /tmp/callmem2
08049000      4K rw---  /tmp/callmem2
08259000    132K rw---    [ anon ]
b7bd8000   4116K rw---    [ anon ]
b7ff0000      8K rw---    [ anon ]
bf938000     84K rw---    [ stack ]
 total     5728K
 
再次查看内存的利用情况,free还是5MB,这回cached释放了3MB,buffers释放了1MB
[root@test1 ~]# free -m 
             total       used       free     shared    buffers     cached
Mem:           503        497          5          0         27        134
-/+ buffers/cache:        335        167
Swap:         1027          0       1027


4)内存限制的总结:

4.1)三种方法都可以防止OOM的发生.
4.2)overcommit的方法,只有超级用户都能使用,设成2后,之后所有用户的进程都不能消耗超过实际内存的地址空间,也就收不到OOM信息了.但这种方式并不可控.
4.3)ulimit的方法,所有用户都能使用,设定后,当前shell生效,有硬限制和软限制两种方式,这种方式可以控制进程最多占用的内存.
4.4)setrlimit的方法,所有用户都能使用,设定后,当前进程有效.有硬限制和软限制两种方式,这种方式也可以控制进程最多占用的内存.
4.5)setrlimit一般和sysconf一起联用,但sysconf得到的页面数量只是一个模糊的数,在一个繁忙的系统里,这个值是不定的.
4.6)最后说一点,sysconf(_SC_PAGE_SIZE)得到的值是一个可用物理内存的值,而不包括swap.
4.6)如果用ulimit设定当前shell下启用的进程最多可用10MB,即使sysconf(_SC_PAGE_SIZE);得到的值是大于10MB,我们也只能用到10MB.例如下面的试验:

[root@test1 tmp]# ulimit -d 10240 -m 10240 -v 10240

[root@test1 tmp]# ./callmem2 
crrent hard limit is 10 MB
setrlimit: Invalid argument
limit set to 328 MB
malloced 1 MB
malloced 2 MB
malloced 3 MB
malloced 4 MB
malloced 5 MB
malloced 6 MB
malloced 7 MB
malloc: Cannot allocate memory
paused

[1]+  Stopped                 ./callmem2
[root@test1 tmp]# jobs -x pmap %1
2514:   ./callmem2
0067b000      4K r-x--    [ anon ]
00b51000    100K r-x--  /lib/ld-2.5.so
00b6a000      4K r-x--  /lib/ld-2.5.so
00b6b000      4K rwx--  /lib/ld-2.5.so
00b6e000   1244K r-x--  /lib/libc-2.5.so
00ca5000      8K r-x--  /lib/libc-2.5.so
00ca7000      4K rwx--  /lib/libc-2.5.so
00ca8000     12K rwx--    [ anon ]
08048000      4K r-x--  /tmp/callmem2
08049000      4K rw---  /tmp/callmem2
b7729000   8224K rw---    [ anon ]
b7f44000      8K rw---    [ anon ]
bfd46000     84K rw---    [ stack ]
 total     9704K

阅读(1258) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~