Linux下的buffers/cache/swap


buffers/cache

free命令

Linux下的free命令是显示当前内存的使用,-m代表用MB字节显示,-g代表用GB字节显示:

# free -m
             total       used       free     shared    buffers     cached
Mem:         48258      48008        250          0         30      33333
-/+ buffers/cache:      14643      33614
Swap:        24199       6245      17954  

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         46          0          0          0         32
-/+ buffers/cache:         14         32
Swap:           23          6         17

参数详解

第一部分Mem行:

  • total: 内存总数: 48258MB -> 47G
  • used: 已经使用的内存数: 48008 -> 46G
  • free: 空闲的内存数: 250MB -> 0G
  • shared: 当前已经废弃不用,总是0: 0MB -> 0G
  • buffers: 缓存内存数: 30MB -> 0G
  • cached: 缓存内存数: 33333MB -> 32G

可以看到free(空闲的内存数)的内存数只有250MB,内存大部分被buffers/cache使用了,这只是对 操作系统 层面来说的。在Linux系统中有这么一种思想,内存不用白不用,因此它尽可能的cache和buffer一些数据,以便于提高IO性能。但实际上这些内存应用程序还是可以立刻拿来使用的。

第二部分(-/+ buffers/cache)行:

在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓存,用于缓存系统读写过的数据文件,这样可以提高IO性能。

上面的 -/+ buffers/cache 可以分为: -buffers/cache+buffers/cache

  • -buffers/cache: used内存数: 14643MB -> 14G
  • +buffers/cache: free内存数: 33614MB -> 32G

可见-buffers/cache 反映的是被程序实实在在吃掉的内存总数(14G),而 +buffers/cache: 反映的是可以使用的内存总数(32G)。

也就是对操作系统来说: buffers/cache 都是被使用的,所以它认为 free 都只有250MB。但是对 应用程序 来说: freebuffers/cache 是可以使用的,如果调用内存分配函数(mallocnew),这部分内存还是可以分配的。

测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
char *buf = NULL;
const int count = 16 * 1024;
const int buf_size = 1024 * 1024;
int i;

for(i = 0; i < count; ++i) {
if((buf = malloc(sizeof(char) * buf_size)) == NULL) {
printf("malloc failed\n");
return -1;
}
memset(buf, 'm', buf_size);

printf("malloc successful\n");
}

printf("waiting stop......\n");

while(1);

return 0;
}

上面的程序很简单,每次申请 1024 * 1024(1M) 的内存,申请 16 * 1024次, 也就是总共申请了 16G 的内存。在每次申请完内存后调用memset函数, 注意 ,如果在申请完内存后。如果不使用这块内存的话,系统是不会给你的程序分配内存的,也就是说只有你真正使用到了这块内存,系统才会为你的程序分配,这就是上面为什么要使用memset函数的原因。

编译程序:

gcc test.c

查看程序运行前的内存使用情况,并运行程序:

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         46          0          0          0         32
-/+ buffers/cache:         14         32
Swap:           23          6         17
# 
# ./a.out

输出结果:

malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
malloc successful
waiting stop......

使用free查看内存的使用情况:

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         46          0          0          0         16
-/+ buffers/cache:         30         16
Swap:           23          6         17

可以看到在上面 free 只有 0G 的情况下,程序依然可以申请成功(16G内存), cached 由原先的 32g 减少到了 16G 。 也就是上面所说的,cached 对应用程序来说,是可以真实使用内存的。

终止程序,查看内存的使用情况:

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         30         16          0          0         16
-/+ buffers/cache:         14         32
Swap:           23          6         17

可以看到在上面 free 变成了 16G由此可见,cached的内存是可以被程序使用的

什么时候会增加cached的使用量?

上面说了,cached 主用于缓存系统读写过的数据文件。下面写个程序,在测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
const int count = 16 * 1024 * 1024;
char buf[1024];
FILE *fp = NULL;
int i;

memset(buf, 'm', 1024);

if((fp = fopen("out.bin", "a+b")) == NULL) {
printf("fopen error\n");
return -1;
}

for(i = 0; i < count; ++i) {
if(fwrite(buf, 1, 1024, fp) != 1024) {
printf("fwrite error\n");
return -1;
}
}

return 0;
}

程序很简单,最后的结果就是创建一个 16Gout.bin文件。

运行程序前,查看内存使用的情况,程序运行结束后,创建了一个16G的文件:

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         30         16          0          0         16
-/+ buffers/cache:         14         32
Swap:           23          6         17
#
# ./a.out
#    
# ll out.bin -h
-rw-r--r-- 1 root root 16G Apr 27 17:10 out.bin

程序运行结束后,使用free查看内存的使用情况:

# free -g
                 total       used       free     shared    buffers     cached
Mem:            47         46          0          0          0         32
-/+ buffers/cache:         14         32
Swap:           23          6         17
# 

可以看到 free 由原先的 16G 变为了 0Gcached 从原先的 16G 增加到了 32G由此可见,在读写文件的时候,内核使用内存作为缓存,这就导致了cached的增加,但是这部分内存,对于应用程序来说还是可以使用的。

清除cached

cached 也是可以手动清除的,但是伴随着 cached 的清除, 会使系统的IO飚高、系统的性能会急剧下降。

下面这个文件可以用来清除 cached

# cat /proc/sys/vm/drop_caches 
0

使用下面的操作:

echo 1 > /proc/sys/vm/drop_caches
  • echo 1 > /proc/sys/vm/drop_caches: 表示清除pagecache。
  • echo 2 > /proc/sys/vm/drop_caches: 表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核管理内存的一种机制。
  • echo 3 > /proc/sys/vm/drop_caches: 表示清除pagecache和slab分配器中的对象。

清除cached:

# echo 3 > /proc/sys/vm/drop_caches 
# cat /proc/sys/vm/drop_caches 
3
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         18         29          0          0          4
-/+ buffers/cache:         13         33
Swap:           23          6         17

cached 从原先的 32G 减少到了 4Gfree 增加到了 29G

为什么cached没有被完全清除呢? 有哪些cached是不可以被回收呢?

tmpfs

Linux提供了一种文件系统叫做 tmpfs, 它可以将内存的一部分空间拿来当做文件系统使用,使内存空间可以当做文件来用。手工创建一个 tmpfs

# mkdir /tmp/tmpfs
# mount -t tmpfs -o size=16G none /tmp/tmpfs
# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/mapper/VolGroup-lv_root
                      51606140  48358024    626676  99% /
tmpfs                 24708300   1729180  22979120   7% /dev/shm
/dev/sda1               495844     38259    431985   9% /boot
/dev/mapper/VolGroup-lv_home
                     1045411708 696662912 295644976  71% /home
/home/iso/rhel-server-6.4-x86_64-dvd.iso
                       3632776   3632776         0 100% /mnt/redhat-iso
none                  16777216         0  16777216   0% /tmp/tmpfs

上面创建了一个新的 tmpfs,它的空间是 16G。 现在可以在/tmp/tmpfs中创建一个 16G 以内的文件,看它占用的是哪部分内存?

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         19         28          0          0          5
-/+ buffers/cache:         13         33
Swap:           23          6         17
# 
# 
# dd if=/dev/zero of=/tmp/tmpfs/testfile bs=1G count=16
16+0 records in
16+0 records out
17179869184 bytes (17 GB) copied, 19.5241 s, 880 MB/s
#        
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         35         12          0          0         21
-/+ buffers/cache:         13         33
Swap:           23          6         17

tmpfs目录下创建了一个 16G 的文件,并通过前后的free命令的对比发现,cached 增长了 16G。 现在收到清除 cached

# echo 3 > /proc/sys/vm/drop_caches 
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         35         12          0          0         21
-/+ buffers/cache:         13         33
Swap:           23          6         17

可以看到, cached 占用的空间并没有被释放,其中 16G 的空间仍然被/tmp/tmpfs中的文件占用。那么tmpfs占用的空间什么时候会被释放呢?是在文件被删除的时候,如果不删除文件,无论内存耗尽到什么程度,内核都不会自动帮你把tmpfs中文件删除,来释放 cached 的空间。

# rm -rf /tmp/tmpfs/testfile 
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         19         28          0          0          5
-/+ buffers/cache:         13         33
Swap:           23          6         17

这是cached第一种不能被回收的情况。

共享内存

共享内存是系统提供一种常用的进程间通信(IPC)方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main(int argc, char *argv[]) {
char *buf = NULL;
int shmid;
size_t buf_size = 2 * 1024 * 1024 * 1024u;

if((shmid = shmget(0x112234, buf_size, IPC_CREAT | 0666)) < 0) {
printf("shmget error\n");
return -1;
}

if((buf = shmat(shmid, NULL, 0)) == (void *)-1) {
printf("shmat error");
return -1;
}

memset(buf, 'm', buf_size);

shmdt(buf);

return 0;
}

程序功能很简单,就是申请了一段 2G 共享内存,然后退出程序,但是在退出之前并没有删除这段共享内存。

# free -g
             total       used       free     shared    buffers     cached
Mem:            47         17         29          0          0          3
-/+ buffers/cache:         13         33
Swap:           23          6         17
# 
# ./a.out 
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         19         27          0          0          5
-/+ buffers/cache:         13         33
Swap:           23          6         17

cached 空间由 3G 增加到了 5G。那么这段 cached 能被回收吗?

# echo 3 > /proc/sys/vm/drop_caches 
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         19         27          0          0          5
-/+ buffers/cache:         13         33
Swap:           23          6         17

结果是仍然不可回收。这段共享内存即使没人使用,仍然会长期存放在 cached, 直到其被删除。

# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x73005aaa 5111808    root       644        4961048    0                       
0x73005aab 5144577    root       644        6328       0                       
0x0063a001 11304962   root       666        84399200   4                       
0x0064a001 20873219   root       666        986174560  19                      
0x0063a002 11337732   root       666        84399200   3                       
0x0063a003 11370501   root       666        84399200   6                       
0x0063a008 11403270   root       666        10998880   2                       
0x0063a009 11436039   root       666        1561696    2                       
0x0063a004 11468808   root       666        42456160   4                       
0x0063a005 11501577   root       666        21484640   3                       
0x0063a007 11534346   root       666        42456160   4                       
0x0063a006 11567115   root       666        84399200   4                       
0x0064a002 20905996   root       666        52941920   18                      
0x7b5189bc 1277965    oracle     660        1612709888 348                     
0x01020002 491534     root       600        2048000000 1                       
0x73020064 19922959   root       644        4961048    1                       
0x73020063 19955728   root       644        6328       1                       
0x00112234 20938769   root       666        2147483648 0                       
# 
# 
# ipcrm -m 20938769
# 
# free -g
             total       used       free     shared    buffers     cached
Mem:            47         17         29          0          0          3
-/+ buffers/cache:         13         33
Swap:           23          6         17

删除共享内存后,cached 被正常释放了。

总结

  • 在Linux系统中,内存是用来用的,不是用来看的。free空间变少,cached 占用很高,并不代表程序的可用内存减少,cached 中的大部分内存,是可以给应用程序malloc使用。只有tmpfs、共享内存才会真正占用 cached 的内存空间。
  • tmpfs中存储的文件会占用 cached,除非文件删除,否则这个 cached 不会自动释放。
  • 共享内存会占用 cached 空间,除非共享内存被删除,否则这个 cached 不会自动释放。
  • cached 可以手动清除,但是清除后会影响IO,导致系统性能下降。

swap

swap分区主要是当物理内存不足时,可以将一部分内存页进行swap操作,swap分区由硬盘提供。但是对于需要高并发、高吞吐的应用来说,磁盘IO通常会成为系统瓶颈。 在Linux中,并不是要等到所有物理内存都使用完才会使用到swap ,系统参数swppiness会决定操作系统使swap的倾向程度。swappiness的取值范围是0~100,swappiness的值越大,说明操作系统可能使用swap的概率越高,swappiness值越低,表示操作系统更加倾向于使用物理内存。swap的默认值是60。

swappines的重要值:

  • 0:Linux3.5以及以上:宁愿用OOM killer也不用swap。在Linux3.4以及更早:宁愿用swap也不用OOM killer
  • 1:Linux3.5以及以上:宁愿用swap也不用OOM killer
  • 60: 默认值
  • 100: 操作系统会主动会用swap

OOM(Out Of Memory)killer机制是指Linux操作系统发现可用内存不足时,强制杀死一些用户进程(非内核进程),来保证系统有足够的可用内存进行分配。

设置方法

echo {bestvalue} > /proc/sys/vm/swappiness

但是上述方法在系统重启后就会失效,为了让配置在重启Linux操作系统后立即生效,只需要在/etc/sysctl.conf追加vm.swappiness={bestvalue}
可。

echo vm.swappiness={bestvalue} >> /etc/sysctl.conf

查看swap的总体情况

Linux提供了free命令来查询操作系统的内存使用情况,其中也包含了swap的相关使用情况。

最后一行的swap统计,从执行结果看,swap一共有2044MB,使用了0MB,空闲2044MB。

$ free -m
            total       used       free     shared    buffers     cached
Mem:          1983        989        994          8         70        374
-/+ buffers/cache:        544       1438
Swap:         2044          0       2044

实时查看swap的使用

Linux提供了vmstat命令查询系统的相关性能指标,其中包含负载、CPU、内存、swap、IO的相关属性。但其中和swap有关的指标是si和so,它们分别代表操作系统的swap inswap out。下面是执行vmstat(每隔一秒输出)的效果,可以看到siso都为0,代表当前没有使用swap

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0      0 975644  72376 394948    0    0  1596    34  134  862  7  4 88  0  0
0  0      0 975684  72376 394948    0    0     0     0  129  388  4  0 96  0  0
0  0      0 975684  72376 394948    0    0     0     0  152  644  6  1 93  0  0
0  0      0 975684  72376 394948    0    0     0     0  107  428  8  1 91  0  0
0  0      0 975684  72376 394948    0    0     0     0  128  701 10  1 89  0  0
0  0      0 975684  72376 394948    0    0     0     0  334  719  9  1 89  0  0
0  0      0 975684  72376 394948    0    0     0     0  312  582  4  3 93  0  0
0  0      0 975684  72376 394948    0    0     0     0  225  437  5  1 94  0  0

查看指定进程的swap使用情况

Linux操作系统中,/proc/{pid}目录是存储指定进程的相关信息,其中/proc/{pid}/smaps记录了当前进程所对应的内存映像信息,可以查询到指定进程的swap使用情况。

$ cat /proc/2941/smaps | grep -i Swap
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB

其中Swap字段代表该内存块存在swap分区的数据大小。通过执行如下命令,就可以找到每个内存块镜像信息中,这个进程使用到的swap量,通过求和就可以算出总的swap用量。

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
const int count = 16 * 1024;
const int buf_size = 1024 * 1024;
char *buf = NULL;
int i;

for(i = 0; i < count; ++i) {
if((buf = malloc(sizeof(char) * buf_size)) == NULL) {
return -1;
}
memset(buf, 'm', buf_size);

printf("malloc: %d MB\n", i);
}
printf("waiting.......\n");

while(1);

return 0;
}

这个程序的功能,主要是每次申请 1M 的内存,总共申请 16G, 但是系统可以使用的内存如下: free(994M)、cached(374M),加起来才1G。

$ free -m
            total       used       free     shared    buffers     cached
Mem:          1983        989        994          8         70        374
-/+ buffers/cache:        544       1438
Swap:         2044          0       2044

编译运行程序,使用vmstat实时的观察swap分区,可以看到si、so不断的在增大:

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
1  0 316892 1538192  19744 139748    8    0     8     0  143 1456  8  3 89  0  0
2  0 316892 1538192  19744 139748    0    0     0     0  116  897  8  3 89  0  0
1  0 316884 1538192  19752 139744    4    0     4    64   94  502  4  1 94  1  0
1  0 316876 1208056  19876 139748   12    0   148     0  174 2956 11 19 69  1  0
1  0 316868  78116  19876 139752   20    0    20     0  497 9447 38 62  0  0  0
2 11 654592  13336     88   6960 1604 1385944 50956 1386080 2228 9403 12 84  0  3  0
3  1 1897088  13676    336  29392 9960 206812 58616 206828 5346 13090  6 86  0  8  0
3  1 2075384  13584     84  12292  212 176516  7752 176516 2853 4154 16 84  0  0  0
0  0 331536 1693028    640  29544 1036 17968 39140 18024  948 2009  4 36 41 1

在swap分区使用完后,程序会被Killed:

malloc: 3355 MB
malloc: 3356 MB
malloc: 3357 MB
malloc: 3358 MB
malloc: 3359 MB
malloc: 3360 MB
malloc: 3361 MB
malloc: 3362 MB
malloc: 3363 MB
Killed

总结

swap分区主要是当物理内存不足时,Linux系统可以把部分inactive的内存页置换到swap分区。但是,并不是要等到所有物理内存都使用完才会使用到swap。这取决于内核的LRU调度算法。如果用free命令,看到swap有被使用,可以使用vmstat实时的查看swap,也可以查看指定的进程的是否有用到swap