> 시스템 튜토리얼 > 리눅스 > Linux 메모리의 캐시가 실제로 재활용될 수 있습니까?

Linux 메모리의 캐시가 실제로 재활용될 수 있습니까?

王林
풀어 주다: 2024-02-11 13:40:03
앞으로
1142명이 탐색했습니다.

머리말

Linux 시스템에서는 시스템 메모리의 사용 상태를 확인하기 위해 free 명령을 자주 사용합니다. RHEL6 시스템에서 free 명령의 표시 내용은 대략 다음과 같습니다.

으아아아

여기서 기본 표시 단위는 kb입니다. 제 서버의 메모리는 128G이므로 숫자가 상대적으로 크게 나타납니다. 이 명령은 리눅스를 사용해 본 거의 모든 사람이 꼭 알아야 하는 명령이지만, 그런 명령이 많을수록 실제로 이해하는 사람이 적어지는 것 같습니다(비율이 적다는 뜻입니다). 일반적인 상황에서 이 명령의 출력에 대한 이해는 다음 수준으로 나눌 수 있습니다.

  1. 이해가 안 돼요. 그런 사람들의 첫 번째 반응은 다음과 같습니다. 맙소사, 70G가 넘는 메모리를 많이 사용하는데 큰 프로그램을 거의 실행하지 않습니까? 왜 이런 일이 발생합니까? 리눅스는 너무 많은 메모리를 차지합니다!
  2. 잘 알고 있다고 생각하세요. 그런 사람들은 대개 자율 학습과 평가를 마친 후에 이렇게 말합니다. 글쎄요, 내 전문적인 관점에 따르면 메모리는 17G 정도이고 아직 사용 가능한 메모리가 많이 남아 있습니다. 버퍼/캐시는 많은 부분을 차지합니다. 즉, 시스템에 파일을 읽고 쓰는 프로세스가 있지만 문제가 되지 않습니다. 메모리의 이 부분은 비어 있을 때 사용됩니다.
  3. 정말로 이해합니다. 이런 사람들의 반응은 사람들이 Linux를 가장 이해하지 못한다고 느끼게 만듭니다. 그들의 반응은 다음과 같습니다. 이것이 무료 쇼입니다. 알겠습니다. 무엇? 이 추억으로 충분하냐고 묻는다면 당연히 모르겠어요! 당신의 프로그램을 작성하는 방법을 어떻게 알 수 있습니까?

현재 인터넷에 있는 기술 문서의 내용에 따르면 Linux에 대해 조금 아는 대다수의 사람들은 두 번째 수준에 있어야 한다고 생각합니다. 일반적으로 버퍼가 차지하고 캐시된 메모리 공간은 메모리 압력이 높을 때 여유 공간으로 해제될 수 있다고 믿어집니다. 하지만 정말 그런가요? 이 주제를 논의하기 전에 버퍼와 캐시의 의미를 간략하게 소개하겠습니다.

Linux 메모리의 캐시가 실제로 재활용될 수 있습니까?

버퍼/캐시란 무엇인가요?

버퍼와 캐시는 컴퓨터 기술에서 과도하게 사용되는 두 가지 용어이며 상황에 따라 다른 의미를 갖습니다. Linux 메모리 관리에서 여기서 버퍼는 Linux 메모리: 버퍼 캐시를 나타냅니다. 여기서 캐시는 Linux 메모리의 페이지 캐시를 나타냅니다. 중국어로 번역하면 버퍼 캐시, 페이지 캐시라고 할 수 있습니다. 역사적으로 그 중 하나(버퍼)는 io 장치의 쓰기 캐시로 사용되었고, 다른 하나(캐시)는 io 장치의 읽기 캐시로 사용되었습니다. 여기서 io 장치는 주로 블록 장치 파일과 파일 상의 일반 파일을 의미합니다. 체계. 그러나 이제 그 의미는 달라졌습니다. 현재 커널에서 페이지 캐시는 이름에서 알 수 있듯이 메모리 페이지를 위한 캐시입니다. 직설적으로 말하면, 페이지별로 할당 및 관리되는 메모리가 있으면 페이지 캐시를 캐시로 사용하여 관리하고 사용할 수 있습니다. 물론, 모든 메모리가 페이지 단위로 관리되는 것은 아니며, 대부분은 블록 단위로 관리됩니다. 메모리의 이 부분에 캐시 기능을 사용하게 되면 버퍼 캐시에 집중됩니다. (이러한 관점에서는 버퍼 캐시를 블록 캐시로 이름을 바꾸는 것이 더 나을까요?) 그러나 모든 블록이 고정된 길이를 갖는 것은 아닙니다. 시스템의 블록 길이는 주로 사용되는 블록 장치와 페이지에 따라 결정됩니다. 길이는 32비트이든 64비트이든 X86에서 4k입니다.

이 두 캐시 시스템의 차이점을 이해하면 어떤 용도로 사용될 수 있는지 이해할 수 있습니다.

페이지 캐시란 무엇입니까

페이지 캐시는 주로 파일 시스템에서 파일 데이터의 캐시로 사용되며, 특히 프로세스가 파일에 대한 읽기/쓰기 작업을 수행할 때 더욱 그렇습니다. 잘 생각해보면 파일을 메모리에 매핑할 수 있는 시스템 호출인 mmap으로 페이지 캐시도 사용해야 하는 게 당연한 걸까요? 현재 시스템 구현에서 페이지 캐시는 다른 파일 형식에 대한 캐싱 장치로도 사용되므로 실제로 페이지 캐시는 대부분의 블록 장치 파일을 캐싱하는 역할도 담당합니다.

什么是buffer cache

Buffer cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。这意味着某些对块的操作会使用buffer cache进行缓存,比如我们在格式化文件系统的时候。一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。

如何回收cache?

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能被回收的情况,那么有没有不能被回收的cache呢?当然有。我们先来看第一种情况:

tmpfs

大家知道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本身的功能描述上是看不出来的。从字面上看,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,也并不是对系统来说没有成本的。总结一下要点,我们应该记得这样几点:

  1. 캐시가 파일 캐시로 해제되면 IO가 높아집니다. 이는 파일 액세스 속도를 높이기 위해 캐시가 지불해야 하는 비용입니다.
  2. tmpfs에 저장된 파일은 캐시 공간을 차지하며, 파일을 삭제하지 않는 한 캐시는 자동으로 해제되지 않습니다.
  3. shmget을 사용하여 적용된 공유 메모리는 캐시 공간을 차지합니다. 공유 메모리가 ipcrm이거나 shmctl을 IPC_RMID로 사용하지 않는 한 관련 캐시 공간은 자동으로 해제되지 않습니다.
  4. mmap 방법을 사용하여 MAP_SHARED 플래그가 적용된 메모리는 캐시 공간을 차지합니다. 프로세스가 이 메모리를 문맵핑하지 않으면 관련 캐시 공간이 자동으로 해제되지 않습니다.
  5. 실제로 shmget과 mmap의 공유 메모리는 커널 계층에서 tmpfs를 통해 구현되고, tmpfs로 구현된 스토리지는 캐시를 사용한다.

이 점을 이해한 후, 우리가 언급한 자유 명령에 대한 모든 이해가 세 번째 수준에 도달할 수 있기를 바랍니다. 메모리 사용은 단순한 개념이 아니며, 캐시는 실제로 여유 공간으로 사용될 수 없다는 점을 이해해야 합니다. 시스템의 메모리가 합리적으로 사용되고 있는지 진정으로 깊이 이해하려면 더 많은 세부 지식을 이해하고 관련 비즈니스 구현에 대해 더 세부적인 판단을 내려야 합니다. 현재 실험 시나리오는 Centos 6 환경입니다. Linux 버전에 따라 실제 무료 상태가 다를 수 있으므로 직접 확인해 보세요.

물론, 이 글에서 설명하는 내용이 캐시를 해제할 수 없는 모든 상황은 아닙니다. 그렇다면 귀하의 애플리케이션 시나리오에서 캐시가 해제될 수 없는 시나리오는 무엇입니까?

위 내용은 Linux 메모리의 캐시가 실제로 재활용될 수 있습니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:lxlinux.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿