Linux 시스템에서는 시스템 메모리의 사용 상태를 확인하기 위해 free 명령을 자주 사용합니다. RHEL6 시스템에서 free 명령의 표시 내용은 대략 다음과 같습니다.
으아아아여기서 기본 표시 단위는 kb입니다. 제 서버의 메모리는 128G이므로 숫자가 상대적으로 크게 나타납니다. 이 명령은 리눅스를 사용해 본 거의 모든 사람이 꼭 알아야 하는 명령이지만, 그런 명령이 많을수록 실제로 이해하는 사람이 적어지는 것 같습니다(비율이 적다는 뜻입니다). 일반적인 상황에서 이 명령의 출력에 대한 이해는 다음 수준으로 나눌 수 있습니다.
현재 인터넷에 있는 기술 문서의 내용에 따르면 Linux에 대해 조금 아는 대다수의 사람들은 두 번째 수준에 있어야 한다고 생각합니다. 일반적으로 버퍼가 차지하고 캐시된 메모리 공간은 메모리 압력이 높을 때 여유 공간으로 해제될 수 있다고 믿어집니다. 하지만 정말 그런가요? 이 주제를 논의하기 전에 버퍼와 캐시의 의미를 간략하게 소개하겠습니다.
버퍼와 캐시는 컴퓨터 기술에서 과도하게 사용되는 두 가지 용어이며 상황에 따라 다른 의미를 갖습니다. Linux 메모리 관리에서 여기서 버퍼는 Linux 메모리: 버퍼 캐시를 나타냅니다. 여기서 캐시는 Linux 메모리의 페이지 캐시를 나타냅니다. 중국어로 번역하면 버퍼 캐시, 페이지 캐시라고 할 수 있습니다. 역사적으로 그 중 하나(버퍼)는 io 장치의 쓰기 캐시로 사용되었고, 다른 하나(캐시)는 io 장치의 읽기 캐시로 사용되었습니다. 여기서 io 장치는 주로 블록 장치 파일과 파일 상의 일반 파일을 의미합니다. 체계. 그러나 이제 그 의미는 달라졌습니다. 현재 커널에서 페이지 캐시는 이름에서 알 수 있듯이 메모리 페이지를 위한 캐시입니다. 직설적으로 말하면, 페이지별로 할당 및 관리되는 메모리가 있으면 페이지 캐시를 캐시로 사용하여 관리하고 사용할 수 있습니다. 물론, 모든 메모리가 페이지 단위로 관리되는 것은 아니며, 대부분은 블록 단위로 관리됩니다. 메모리의 이 부분에 캐시 기능을 사용하게 되면 버퍼 캐시에 집중됩니다. (이러한 관점에서는 버퍼 캐시를 블록 캐시로 이름을 바꾸는 것이 더 나을까요?) 그러나 모든 블록이 고정된 길이를 갖는 것은 아닙니다. 시스템의 블록 길이는 주로 사용되는 블록 장치와 페이지에 따라 결정됩니다. 길이는 32비트이든 64비트이든 X86에서 4k입니다.
이 두 캐시 시스템의 차이점을 이해하면 어떤 용도로 사용될 수 있는지 이해할 수 있습니다.
페이지 캐시는 주로 파일 시스템에서 파일 데이터의 캐시로 사용되며, 특히 프로세스가 파일에 대한 읽기/쓰기 작업을 수행할 때 더욱 그렇습니다. 잘 생각해보면 파일을 메모리에 매핑할 수 있는 시스템 호출인 mmap으로 페이지 캐시도 사용해야 하는 게 당연한 걸까요? 현재 시스템 구현에서 페이지 캐시는 다른 파일 형식에 대한 캐싱 장치로도 사용되므로 실제로 페이지 캐시는 대부분의 블록 장치 파일을 캐싱하는 역할도 담당합니다.
Buffer cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。这意味着某些对块的操作会使用buffer cache进行缓存,比如我们在格式化文件系统的时候。一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。
Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。一般情况下,这个操作中主要的内存释放都来自于对buffer/cache的释放。尤其是被使用更多的cache空间。既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。
但是这种清缓存的工作也并不是没有成本。理解cache是干什么的就可以明白清缓存必须保证cache中的数据跟对应文件中的数据一致,才能对cache进行释放。所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。
在系统中除了内存将被耗尽的时候可以清缓存以外,我们还可以使用下面这个文件来人工触发缓存清除的操作:
[root@tencent64 ~]# cat /proc/sys/vm/drop_caches 1
方法是:
echo 1 > /proc/sys/vm/drop_caches
当然,这个文件可以设置的值分别为1、2、3。它们所表示的含义为:echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache。
echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的pagecache。
echo 3 > /proc/sys/vm/drop_caches:表示清除pagecache和slab分配器中的缓存对象。
我们分析了cache能被回收的情况,那么有没有不能被回收的cache呢?当然有。我们先来看第一种情况:
大家知道Linux提供一种“临时”文件系统叫做tmpfs,它可以将内存的一部分空间拿来当做文件系统使用,使内存空间可以当做目录文件来用。现在绝大多数Linux系统都有一个叫做/dev/shm的tmpfs目录,就是这样一种存在。当然,我们也可以手工创建一个自己的tmpfs,方法如下:
[root@tencent64 ~]# mkdir /tmp/tmpfs [root@tencent64 ~]# mount -t tmpfs -o size=20G none /tmp/tmpfs/ [root@tencent64 ~]# df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda1 10325000 3529604 6270916 37% / /dev/sda3 20646064 9595940 10001360 49% /usr/local /dev/mapper/vg-data 103212320 26244284 71725156 27% /data tmpfs 66128476 14709004 51419472 23% /dev/shm none 20971520 0 20971520 0% /tmp/tmpfs
于是我们就创建了一个新的tmpfs,空间是20G,我们可以在/tmp/tmpfs中创建一个20G以内的文件。如果我们创建的文件实际占用的空间是内存的话,那么这些数据应该占用内存空间的什么部分呢?根据pagecache的实现功能可以理解,既然是某种文件系统,那么自然该使用pagecache的空间来管理。我们试试是不是这样?
[root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 36 89 0 1 19 -/+ buffers/cache: 15 111 Swap: 2 0 2 [root@tencent64 ~]# dd if=/dev/zero of=/tmp/tmpfs/testfile bs=1G count=13 13+0 records in 13+0 records out 13958643712 bytes (14 GB) copied, 9.49858 s, 1.5 GB/s [root@tencent64 ~]# [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 49 76 0 1 32 -/+ buffers/cache: 15 110 Swap: 2 0 2
我们在tmpfs目录下创建了一个13G的文件,并通过前后free命令的对比发现,cached增长了13G,说明这个文件确实放在了内存里并且内核使用的是cache作为存储。再看看我们关心的指标: -/+ buffers/cache那一行。我们发现,在这种情况下free命令仍然提示我们有110G内存可用,但是真的有这么多么?我们可以人工触发内存回收看看现在到底能回收多少内存:
[root@tencent64 ~]# echo 3 > /proc/sys/vm/drop_caches [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 43 82 0 0 29 -/+ buffers/cache: 14 111 Swap: 2 0 2
可以看到,cached占用的空间并没有像我们想象的那样完全被释放,其中13G的空间仍然被/tmp/tmpfs中的文件占用的。当然,我的系统中还有其他不可释放的cache占用着其余16G内存空间。那么tmpfs占用的cache空间什么时候会被释放呢?是在其文件被删除的时候.如果不删除文件,无论内存耗尽到什么程度,内核都不会自动帮你把tmpfs中的文件删除来释放cache空间。
[root@tencent64 ~]# rm /tmp/tmpfs/testfile [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 30 95 0 0 16 -/+ buffers/cache: 14 111 Swap: 2 0 2
这是我们分析的第一种cache不能被回收的情况。还有其他情况,比如:
共享内存是系统提供给我们的一种常用的进程间通信(IPC)方式,但是这种通信方式不能在shell中申请和使用,所以我们需要一个简单的测试程序,代码如下:
[root@tencent64 ~]# cat shm.c #include #include #include #include #include #include #define MEMSIZE 2048 *1024*1023 int main() { int shmid; char *ptr; pid_t pid; struct shmid_ds buf; int ret; shmid = shmget(IPC_PRIVATE, MEMSIZE, 0600); if (shmid"shmget()"); exit(1); } ret = shmctl(shmid, IPC_STAT, &buf); if (ret "shmctl()"); exit(1); } printf("shmid: %d\n", shmid); printf("shmsize: %d\n", buf.shm_segsz); buf.shm_segsz *= 2; ret = shmctl(shmid, IPC_SET, &buf); if (ret "shmctl()"); exit(1); } ret = shmctl(shmid, IPC_SET, &buf); if (ret "shmctl()"); exit(1); } printf("shmid: %d\n", shmid); printf("shmsize: %d\n", buf.shm_segsz); pid = fork(); if (pid"fork()"); exit(1); } if (pid==0) { ptr = shmat(shmid, NULL, 0); if (ptr==(void*)-1) { perror("shmat()"); exit(1); } bzero(ptr, MEMSIZE); strcpy(ptr, "Hello!"); exit(0); } else { wait(NULL); ptr = shmat(shmid, NULL, 0); if (ptr==(void*)-1) { perror("shmat()"); exit(1); } puts(ptr); exit(0); } }
程序功能很简单,就是申请一段不到2G共享内存,然后打开一个子进程对这段共享内存做一个初始化操作,父进程等子进程初始化完之后输出一下共享内存的内容,然后退出。但是退出之前并没有删除这段共享内存。我们来看看这个程序执行前后的内存使用:
[root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 30 95 0 0 16 -/+ buffers/cache: 14 111 Swap: 2 0 2 [root@tencent64 ~]# ./shm shmid: 294918 shmsize: 2145386496 shmid: 294918 shmsize: -4194304 Hello! [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 32 93 0 0 18 -/+ buffers/cache: 14 111 Swap: 2 0 2 cached空间由16G涨到了18G。那么这段cache能被回收么?继续测试: [root@tencent64 ~]# echo 3 > /proc/sys/vm/drop_caches [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 32 93 0 0 18 -/+ buffers/cache: 14 111 Swap: 2 0 2
结果是仍然不可回收。大家可以观察到,这段共享内存即使没人使用,仍然会长期存放在cache中,直到其被删除。删除方法有两种,一种是程序中使用shmctl()去IPC_RMID,另一种是使用ipcrm命令。我们来删除试试:
[root@tencent64 ~]# ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00005feb 0 root 666 12000 4 0x00005fe7 32769 root 666 524288 2 0x00005fe8 65538 root 666 2097152 2 0x00038c0e 131075 root 777 2072 1 0x00038c14 163844 root 777 5603392 0 0x00038c09 196613 root 777 221248 0 0x00000000 294918 root 600 2145386496 0 [root@tencent64 ~]# ipcrm -m 294918 [root@tencent64 ~]# ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00005feb 0 root 666 12000 4 0x00005fe7 32769 root 666 524288 2 0x00005fe8 65538 root 666 2097152 2 0x00038c0e 131075 root 777 2072 1 0x00038c14 163844 root 777 5603392 0 0x00038c09 196613 root 777 221248 0 [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 30 95 0 0 16 -/+ buffers/cache: 14 111 Swap: 2 0 2
删除共享内存后,cache被正常释放了。这个行为与tmpfs的逻辑类似。内核底层在实现共享内存(shm)、消息队列(msg)和信号量数组(sem)这些POSIX:XSI的IPC机制的内存存储时,使用的都是tmpfs。这也是为什么共享内存的操作逻辑与tmpfs类似的原因。当然,一般情况下是shm占用的内存更多,所以我们在此重点强调共享内存的使用。说到共享内存,Linux还给我们提供了另外一种共享内存的方法,就是:
mmap()是一个非常重要的系统调用,这仅从mmap本身的功能描述上是看不出来的。从字面上看,mmap就是将一个文件映射进进程的虚拟内存地址,之后就可以通过操作内存的方式对文件的内容进行操作。但是实际上这个调用的用途是很广泛的。当malloc申请内存时,小段内存内核使用sbrk处理,而大段内存就会使用mmap。当系统调用exec族函数执行时,因为其本质上是将一个可执行文件加载到内存执行,所以内核很自然的就可以使用mmap方式进行处理。我们在此仅仅考虑一种情况,就是使用mmap进行共享内存的申请时,会不会跟shmget()一样也使用cache?
同样,我们也需要一个简单的测试程序:
[root@tencent64 ~]# cat mmap.c #include #include #include #include #include #include #include #include #define MEMSIZE 1024*1024*1023*2 #define MPFILE "./mmapfile" int main() { void *ptr; int fd; fd = open(MPFILE, O_RDWR); if (fd "open()"); exit(1); } ptr = mmap(NULL, MEMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, fd, 0); if (ptr == NULL) { perror("malloc()"); exit(1); } printf("%p\n", ptr); bzero(ptr, MEMSIZE); sleep(100); munmap(ptr, MEMSIZE); close(fd); exit(1); }
这次我们干脆不用什么父子进程的方式了,就一个进程,申请一段2G的mmap共享内存,然后初始化这段空间之后等待100秒,再解除影射所以我们需要在它sleep这100秒内检查我们的系统内存使用,看看它用的是什么空间?当然在这之前要先创建一个2G的文件./mmapfile。结果如下:
[root@tencent64 ~]# dd if=/dev/zero of=mmapfile bs=1G count=2 [root@tencent64 ~]# echo 3 > /proc/sys/vm/drop_caches [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 30 95 0 0 16 -/+ buffers/cache: 14 111 Swap: 2 0 2
然后执行测试程序:
[root@tencent64 ~]# ./mmap & [1] 19157 0x7f1ae3635000 [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 32 93 0 0 18 -/+ buffers/cache: 14 111 Swap: 2 0 2 [root@tencent64 ~]# echo 3 > /proc/sys/vm/drop_caches [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 32 93 0 0 18 -/+ buffers/cache: 14 111 Swap: 2 0 2
我们可以看到,在程序执行期间,cached一直为18G,比之前涨了2G,并且此时这段cache仍然无法被回收。然后我们等待100秒之后程序结束。
[root@tencent64 ~]# [1]+ Exit 1 ./mmap [root@tencent64 ~]# [root@tencent64 ~]# free -g total used free shared buffers cached Mem: 126 30 95 0 0 16 -/+ buffers/cache: 14 111 Swap: 2 0 2
程序退出之后,cached占用的空间被释放。这样我们可以看到,使用mmap申请标志状态为MAP_SHARED的内存,内核也是使用的cache进行存储的。在进程对相关内存没有释放之前,这段cache也是不能被正常释放的。实际上,mmap的MAP_SHARED方式申请的内存,在内核中也是由tmpfs实现的。由此我们也可以推测,由于共享库的只读部分在内存中都是以mmap的MAP_SHARED方式进行管理,实际上它们也都是要占用cache且无法被释放的。
我们通过三个测试例子,发现Linux系统内存中的cache并不是在所有情况下都能被释放当做空闲空间用的。并且也也明确了,即使可以释放cache,也并不是对系统来说没有成本的。总结一下要点,我们应该记得这样几点:
이 점을 이해한 후, 우리가 언급한 자유 명령에 대한 모든 이해가 세 번째 수준에 도달할 수 있기를 바랍니다. 메모리 사용은 단순한 개념이 아니며, 캐시는 실제로 여유 공간으로 사용될 수 없다는 점을 이해해야 합니다. 시스템의 메모리가 합리적으로 사용되고 있는지 진정으로 깊이 이해하려면 더 많은 세부 지식을 이해하고 관련 비즈니스 구현에 대해 더 세부적인 판단을 내려야 합니다. 현재 실험 시나리오는 Centos 6 환경입니다. Linux 버전에 따라 실제 무료 상태가 다를 수 있으므로 직접 확인해 보세요.
물론, 이 글에서 설명하는 내용이 캐시를 해제할 수 없는 모든 상황은 아닙니다. 그렇다면 귀하의 애플리케이션 시나리오에서 캐시가 해제될 수 없는 시나리오는 무엇입니까?
위 내용은 Linux 메모리의 캐시가 실제로 재활용될 수 있습니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!