[Linux] Slab 메모리 줄이기

[Linux] Slab 메모리 줄이기
https://www.geeksforgeeks.org/interesting-facts-about-linux/

[요약]

  1. Slab은 커널 캐시이다.
  2. Slab은 SReclaimable과 SUnreclaim으로 나뉜다. 현재 사용하냐, 안하냐 차이다.
  3. drop cache를 하면 SReclaimable이 줄어든다.
  4. shrink slab을 할 경우 SUnreclaim이 줄어든다. 단, docs에서 shrink slab은 실행 중인 어플리케이션에 영향을 미칠 수 있다고 나와있다.
  5. 하지만 SUnreclaim은 커널이 kmalloc 후 release가 안된 것이라 프로세스를 죽인다고 해서 돌아오지 않으며, 필자는 다른 방법을 찾지 못했다

서버를 모니터링을 할 때 메모리 사용량이 너무 많으면 무엇부터 체크할까?

필자는 먼저 서버에 있는 프로세스가 점유하고 있는 메모리를 먼저 체크한다. 불필요한 프로세스가 돌아가는 경우도 있고, 한 프로세스가 메모리를 너무 크게 사용한다면 메모리 누수를 의심한다.

하지만 프로세스가 메모리를 적게 사용하는데도 메모리 사용량이 큰 경우는 어떤 경우일까? 이때는 커널의 메모리 사용을 의심해볼 수 있다.

이번 글은 커널에서 캐시 역할을 하는 Slab을 알아볼 것이다. Slab에 대한 설명은 다른 블로그에도 좋은 설명이 많으니 간략하게 할 것이다. 대신 서버 메모리에 Slab이 차지하는 양을 체크하는 방법과 그 메모리들을 재사용하는 방법을 알아보겠다.

Slab이란?

Slab은 커널 데이터 객체 생성/파괴의 오버헤드로 인한 성능 저하를 줄이기 위해 자주 사용되는 오브젝트들을 미리 만들어 관리한다. Slab에 할당된 객체가 release 되더라도 재사용을 위해 캐시된 상태로 유지한다.

이해를 하기 위해 전체적인 메모리 할당 구조를 한번 보자. kmalloc() 함수를 통해 커널 내부에서 메모리를 할당받을 때 어떤 일이 일어날까?

출처: https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf

커널은 메모리를 페이지 단위로 관리하고, 커널의 page allocator를 buddy allocator라고 한다. Buddy allocator는 external fragmentation을 줄이기 위해 메모리를 관리한다.

출처: https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf

이 글은 buddy allocator에 대한 글이 아니기 때문에 커널의 page allocator라고만 이해해도 충분하다.

커널은 budddy allocator에 두 가지 메커니즘으로 접근한다.

  • slab allocator - physically contiguous한 메모리를 얻는다.
  • vmalloc - virtually contiguous하지만 physically scattered 할 수 있다

slab allocator는 빠른 특징을 가지고 있고, physical page가 한 개 이하로 필요할 때  주로 사용을 한다. 위 첫번째 그림에서 볼 수 있듯 kmalloc은 이 slab allocator을 사용한다.

Slab의 구조

출처: https://students.mimuw.edu.pl/ZSO/Wyklady/06_memory2/BuddySlabAllocator.pdf

Slab은 slab object들을 가지고 있다. Slab object는 slab이 미리 할당한 메모리 공간이다.

Slab object는 커널에서 자주 사용하는 구조체들을 할당해 가지고 있는다. 예를 들면 task_struct, inode, mm_struct, kmalloc-512, kmalloc-256, ... 등이다.

만약 kmalloc(54)을 호출하면 커널에서 buddy allocator가 관리하는 최소 페이지 단위인 4k를 할당하지 않고 kmalloc-64 슬랩 캐시를 사용해 64만큼 할당해준다.

Slab의 상태

slab은 여러 개 존재하며, 한 slab에 존재하는 object는 모두 같은 종류이다.

출처: https://wiki.amigaos.net/wiki/Exec_Memory_Allocation

Slab은 다음 상태 중 하나로 존재한다

  • empty: slab의 모든 객체가 free로 마킹됨
  • parital: slab의 일부 객체만 used로 마킹됨
  • full: slab의 모든 객체가 used로 마킹 됨

초기에 시스템은 모든 slab을 empty로 마킹을 한다. 프로세스가 새 커널 객체를 호출했을 때, 시스템은 partial slab에서 해당하는 객체가 있는지 확인한다. 만약 없다면 시스템은 새로운 slab을 physical page에 할당해 캐시에 배정한다.

Buffer와 Cache

메모리 사용량을 확인하기 위해 많이 사용하는 명령어로 free -h가 있다. 여기서 나오는 buff/cache는 무엇일까? 이것이 slab과 어떻게 관련이 있을까?

man free에 따르면 정의는 다음과 같다.

  • buffers: 커널 버퍼에 의해 사용되는 메모리
  • cache: page cache와 slabs (Cached and SReclaimable in /proc/meminfo)

유닉스 파일 시스템에서 buffer가 차지하는 영역을 살펴보자.

출처: https://www.slideserve.com/tara/file

buffer는 커널이 I/O 성능 향상을 위해 사용하는 메모리 영역으로,  Super blockinode block에 해당하는 메타데이터를 저장한다.

캐시의 page cache는 파일의 데이터를 캐싱하는 것이고, slab은 위에서 설명한 바 있다.

Slab 메모리 보는 법

sudo cat /proc/slabinfo

커널에서 자주 사용되는 객체들의 캐시를 보여준다.

sudo vmstat -m

slabinfo의 캐시 객체 별로 Num, Total, Size, Pages 를 보여준다

cat /proc/meminfo

위 명령어로 메모리를 조회하면 SReclaimableSUnreclaim을 확인할 수 있다.

  • SReclaimable: reclaim이 될 수 있는 slab의 파트
  • SUnreclaim: Slab이 사용을 하고 있는 영역이므로, 메모리가 꽉 차 더 필요하더라도 release되지 않는다.
  • 리눅스 man-pages

SUnreclaim이 늘어나는 이유는 커널 컴포넌트가 kamlloc으로 메모리를 할당한 후 release를 안해서 그런 것이다.

이 경우 프로세스를 kill해 죽이더라도, SUnreclaim은 줄어들지 않는다(!!!!)

Shrink

커널의 캐시를 줄이는 방법은 다음과 같다.

To free pagecache: echo 1 > /proc/sys/vm/drop_caches

To free reclaimable slab objects (includes dentries and inodes): echo 2 > /proc/sys/vm/drop_caches

To free slab objects and pagecache: echo 3 > /proc/sys/vm/drop_caches

하지만 위 방법은 SUnreclaim을 줄이지 못한다. SUnreclaim을 줄이는 방법은 다음과 같다.

sudo bash -c 'echo 1 > /sys/kernel/slab/kmalloc-4096/shrink

사실 이 명령어는 slab의 kmalloc-4096 오브젝트를 shrink하는 것이다. 더 효과를 보려면 slabinfo를 조회해 다른 kmalloc 오브젝트를 줄이는 것이 도움이 될 수 있다.

혹시 위 명령어는 위험하지 않을까?

2007~2008년 작성된 kernel docs에 shrink에 대한 언급이 있다.

The shrink file is used to reclaim unused slab cache
memory from a cache.  Empty per-cpu or partial slabs
are freed and the partial list is sorted so the slabs
with the fewest available objects are used first.
It only accepts a value of "1" on write for shrinking
the cache. Other input values are considered invalid.
Shrinking slab caches might be expensive and can
adversely impact other running applications.  So it
should be used with care.

다른 실행 중인 어플리케이션에 영향을 미칠 수 있다고 되어 있다. 어쩔 수 없는 상황일 때만 쓰자.

Reference

https://brunch.co.kr/@dreaminz/2

https://brunch.co.kr/@dreaminz/3

http://rousalome.egloos.com/10001242

https://lore.kernel.org/linux-mm/20190723151445.7385-1-longman@redhat.com/

https://en.wikipedia.org/wiki/Slab_allocation

https://wiki.amigaos.net/wiki/Exec_Memory_Allocation

https://community.oracle.com/tech/apps-infra/discussion/3553285/linux-meminfo-very-high-slab

https://www.alibabacloud.com/help/en/elastic-compute-service/latest/identify-the-causes-of-high-percentage-of-the-slab-unreclaimable-memory-in-the-linux-operating-system

https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab