메모리가 부족한 것을 어떻게 확인할 수 있는지, 그리고 부족한 경우에 커널은 어떻게 대처하는지 알아본다.
swap 영역은 디스크의 일부분을 메모리처럼 사용하기 위해 만들어놓은 공간이다. 메모리가 부족할 경우에 프로세스는 swap 영역을 할당받아 사용하게 된다. 물리 메모리가 아니라 디스크를 사용하기 때문에 접근과 처리 속도가 떨어져서 성능 저하가 일어날 수 있다.
swap 영역은 다음과 같이 만들 수 있다.
$ swapon -s // swap 용량 확인
$ fallocate -l 64M /swapfile // swap 파일 생성
$ chmod 600 /swapfile // 권한 조정
$ mkswap /swapfile // swap 형식 적용
$ swapon /swapfile // 임시 적용
free
명령어로 swap 영역이 잘 만들어졌나 확인해본다
$ free -k
total used free shared buff/cache available
Mem: 498372 76440 329404 504 92528 379880
Swap: 65532 52144 13388
swap 메모리를 사용한다는 것은 시스템의 메모리가 부족하다는 뜻이기 때문에 어떤 프로세스가 swap 메모리를 사용하는지 알아내는 것은 시스템의 상황을 개선시키기 위해서 중요하다. 이걸 아는 방법 중 하나로 proc/<pid>/smaps
파일을 확인하는 방법이 있다.
7f7e187ae000-7f7e187af000 rw-p 00000000 00:00 0
Size: 4 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
AnonHugePages: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 4 kB
SwapPss: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd wr mr mw me ac sd
위에서 보는 프로세스는 논리적 메모리 7f7e187ae000-7f7e187af000 사이에 4KB의 크기중에서 4KB가 swap 영역에 속해있는 것을 볼 수 있다. 이런 방법으로는 프로세스의 각 논리적 메모리를 전부 살펴봐야하기 때문에 불편하다. 하나의 프로세스가 사용하는 총 swap 영역을 한눈에 보려면 /proc/<pid>/status
파일을 보는 것이 더 편하다.
Name: stress
State: R (running)
Tgid: 1886
Ngid: 0
Pid: 1886
PPid: 1884
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 64
Groups: 4 20 24 25 27 29 30 44 46 109 110 1000
NStgid: 1886
NSpid: 1886
NSpgid: 1884
NSsid: 1664
VmPeak: 138556 kB
VmSize: 138556 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 131212 kB
VmRSS: 1832 kB
VmData: 131264 kB
VmStk: 132 kB
VmExe: 20 kB
VmLib: 3000 kB
VmPTE: 40 kB
VmPMD: 12 kB
VmSwap: 56 kB
HugetlbPages: 0 kB
Threads: 1
SigQ: 0/1884
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Seccomp: 0
Speculation_Store_Bypass: vulnerable
Cpus_allowed: 7fff
Cpus_allowed_list: 0-14
Mems_allowed: 00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 22
nonvoluntary_ctxt_switches: 383355
이 외에 전체 프로세스별로 사용 중인 swap 영역의 크기를 확인하려면 smem
이라는 유틸을 사용하면 된다.
$ smem -t
PID User Command Swap USS PSS RSS
1885 ubuntu stress --io 4 --vm 2 --vm-b 84 4 8 148
1890 ubuntu stress --io 4 --vm 2 --vm-b 84 4 8 148
1887 ubuntu stress --io 4 --vm 2 --vm-b 84 4 9 152
1889 ubuntu stress --io 4 --vm 2 --vm-b 84 4 9 152
1884 ubuntu stress --io 4 --vm 2 --vm-b 88 0 43 1424
1599 ubuntu /lib/systemd/systemd --user 724 4 58 1616
1664 ubuntu -bash 1412 1304 1379 3040
3605 ubuntu /usr/bin/python /usr/bin/sm 0 6440 6715 8560
1886 ubuntu stress --io 4 --vm 2 --vm-b 52 34220 34239 34456
1888 ubuntu stress --io 4 --vm 2 --vm-b 52 37260 37279 37496
-------------------------------------------------------------------------------
10 1 2664 79244 79747 87192
커널은 버디 시스템을 통해서 프로세스에 메모리를 할당한다. 버디 시스템은 물리 메모리를 연속된(각 4KB) 메모리 영역으로 관리한다.
$ cat /proc/buddyinfo
Node 0, zone DMA 5 205 163 40 10 9 0 0 0 0 0
Node 0, zone DMA32 260 1447 3794 2564 1124 301 20 0 0 0 0
/proc/buddyinfo
로 확인한 현재 시스템의 현재 상황이다. 각 존의 행들은 2의 배수로 나열되어 있다. 즉 각각 연속 1개의 페이지 크기별 버디, 연속 2개의 페이지 크기별 버디, 연속 4개의 페이지 크기별 버디..이다. 이걸 통해서 DMA 존의 가용 영역을 계산해보면 (4KB x 5)+(8KB x 205)+(16KB x 163)+(32KB x 40)+(64KB x 10)+(128KB x 9) = 7340KB 가 되는 것을 볼 수 있다. DMA32 존도 같은 방식으로 계산을 할 수 있고, 모든 존의 가용 영역을 합산하면 free
명령어에서 나온 free 영역과 유사한 값이 나올 것이다.
커널은 메모리의 요청이 발생했을 때 버디 시스템에서 가장 적당한 버디 리스트를 찾아 프로세스에 넘겨준다. 커널은 기본적으로 유휴 메모리가 있을 경우 캐시로 활용하려 하고, 메모리 사용 요청이 증가하면 캐시로 활용하고 있는 메모리를 재할당해서 프로세스에 할당한다. vm.swappiness
나 vm.vfs_cache_pressure
등의 몇 가지 커널 파라미터를 조정하면 이런 커널의 동작 과정을 좀 더 세부적으로 조절할 수 있다.
vm.swappiness
: 커널이 얼마나 공격적으로 메모리 영역을 swap 영역으로 옮기느냐를 결정하는 파라미터이다. 기본값은 60이다.mv.vfs_cache_pressure
: 커널이 메모리를 재할당할 때 PageCache를 더 많이 재할당할지 아니면 디렉터리나 inode 캐시를 더 많이 재할당할지 결정하는 파라미터이다. 기본값은 100이다.시스템이 swap 영역을 사용하고 있다는 것은 두가지로 해석할 수 있다. 첫번째로 시스템에 성능 저하가 일어나고 있다는 것, 두번째로 메모리가 현재의 워크로드를 수용하는데에 부족하다는 사실이다. 즉 시스템이 더 많은 메모리를 필요로 한다고 이해할 수 있는데, 이 때 취할 수 있는 액션은 또한 두가지다. 메모리를 증설하거나, 메모리 누수를 발견해서 해결하는 일이다. 여기서 메모리 누수를 확인하는 것이 더 먼저 행해져야 되는 이유는 메모리 누수가 일어나는 상황에서 메모리 양을 물리적으로 늘려봤자 시간을 조금 더 버는 것일 뿐 다시 메모리 부족 현상이 일어날 수 있기 때문이다.
일반적으로 메모리 누수를 의심할 수 있는 상황이 몇가지 있다. 메모리 사용량을 그래프로 그려봤을 때 시간이 지남에 따라 메모리 사용량이 계속적으로 증가하고 있거나, 평상시와는 다르게 순간적으로 메모리의 사용량이 폭증하는 경우에는 추적이 필요하다. 메모리 누수 디버깅에는 pmap
이나 gdb
와 같은 도구가 도움이 된다.