Swap

Linux 시스템에서는 다양한 용도로 스왑을 사용한다. 일반적으로 부족한 메모리를 보충하기 위한 용도로만 알려져 있지만 아래와 같은 용도로 활용이 되고 있다.

  • 메모리를 많이 사용하는 프로그램을 위해 (가장 일반적인 용도)
  • Hibernation (메모리의 내용을 디스크에 저장해 두기 위한 용도)

일반 노트북이나 랩탑에서 Hibernation을 하기 위해서는 시스템의 메모리 크기보다 큰 스왑 공간이 반드시 필요하다. 메모리의 정보를 모두 디스크에 담아야 하기 때문이다.

  • 예측에서 벗어난 메모리 공간을 사용하는 프로그램에 대비하기 위한 경우
  • 메모리의 효율을 높이기 위해서

스왑 영역은 일반적으로 디스크에 존재하기 때문에 메모리에 비교할 수 없을 정도로 성능이 떨어지지만 이러한 스왑 영역이 메모리 사용 효율을 높일 수 있다. 프로세스가 데이터를 읽어들일 때 메모리에 저장하여 읽게 되고 이러한 작업이 빈번하게 발생할 경우 메모리에 캐시하여 응답속도를 높이는 형태로 동작한다.

즉, 메모리에 캐시할 수 있는 공간이 많으면 많을 수록 효율을 높일 수 있는데 스왑 영역은 프로세스가 예약한 메모리 공간 중에서 사용되지 않는 혹은 당장 필요하지 않는 부분을 저장하여 메모리가 캐시 역할을 할 수 있는 공간을 더 확보 할 수 있도록 한다.

메모리가 많은데도 Swap을 사용합니다

자주 문의 받는 내용 중 하나이다. 분명 시스템의 RAM은 여유가 있음에도 스왑으로 할당해 둔 공간에 데이터를 쓰는 경우가 종종 있다. 사실 크게 문제되지는 않지만 스왑 영역과 데이터를 자주 주고 받게 된다면 디스크 I/O의 성능에 전체 시스템의 성능이 영향을 받는 경우가 있다.

Linux가 메모리가 많음에도 불구하고 스왑공간을 사용하는 이유는 스왑 사용여부를 결정하는 값의 계산식에서 비롯되는데 Linux Kernel의 mm/vmscan.c에는 아래와 같은 코드가 있다.

swap_tendency = mapped_ratio / 2 + distress + sc->swappiness;

스왑을 사용하려는 경향(tendency)을 계산하는데 있어서 mapped_ratio와 distress 그리고 swappiness라는 변수가 영햐을 미친다.

먼저, mapped_ratio는 아래와 같이 계산되는데

 mapped_ratio = ((global_page_state(NR_FILE_MAPPED) +
                  global_page_state(NR_ANON_PAGES)) * 100) / vm_total_pages;

쉽게 말해서 전체 메모리 중에서 프로세스가 사용하고 있는(Mapped) 메모리의 크기에 대한 비율(% 값)이다.

두 번째로 distress 변수는 아래와 같이 표현되며

distress = 100 >> min(zone->prev_priority, priority);

페이지(메모리 저장구조 단위)를 얼마나 많이 스캔하게 될 건지를 뜻한다. 일반적으로 값은 0이며 100에 가까울 수록 스캔해야하는 양이 많아져서 시스템에 문제가 있다는 뜻이다. (커널 코드 주석에서는 Great Trouble이라고 표현되어 있다)

그리고 swappiness 값은 사용자가 직접 수정할 수 있는 변수로 /proc/sys/vm/swappiness에서 확인 할 수 있으며 기본 값은 60이다.

첫 번째 확인했던 수식을 다시금 정리하면 일반적인 경우에 distress는 0이고 swappiness는 60이기 때문에 아래와 같이 표현 된다.

swap_tendency = mapped_ratio / 2 + 0 + 60;

만약, 4GB 메모리를 가진 시스템에서 3GB를 사용 중이라고 가정하면

swap_tendency = (3GB / 4GB * 100) / 2 + 0 + 60 = 97.5

97.5이기 때문에 바로 스왑이 일어나지는 않는다. (100이상이면 발생한다)

swappiness 값에 따른 영향

실질적으로 사용자가 변경할 수 있는 값은 swappiness 변수 이기 때문에 이 값에 따른 스왑이 발생하는 시점을 유추해 볼 필요가 있을 것이다.

먼저 기본 값인 60일 경우에는 언제 스왑이 발생할지 계산해 보기위해서는 swap_tendency가 100이 되는 시점을 찾으면 되는데

100 = mapped_ratio / 2 + distress + swappiness
 mapped_ratio = (100 - distress - swappiness) * 2
 mapped_ratio = (100 - 0 - 60) * 2 = 80

즉, 80% 이상의 메모리를 사용하게 되면 스왑이 발생하게 될 것을 예측 할 수 있다. 다만 distress가 0인 상황에서라는 전제조건이 필요하다.

만약, swappiness 값을 10으로 설정하고 distress가 0이면 mapped_ratio는 200%이기 때문에 스왑이 발생하는 경우의 조건이 성립하지 않지만 distress가 50이면 80%로 계산된다.

그래서

앞의 예제와 같이 distress 값은 유동적이기 때문에 결과적으로 swappiness 값의 설정이 절대적인 결과를 얻는 값으로 사용될 수 없지만 0에 가까울 수록 가급적 스왑을 하지 않으려 할 것이고 100에 가까울 수록 스왑을 하도록 설정할 수 있다는 부분은 어렵지 않게 알 수 있다.

추가적으로

자주 질문 받던 내용을 정리하기 위해 다시금 리눅스 소스코드를 열어보았다가 발견한 사실로 위에서 언급한 계산 식이 vmscan.c 코드에서 사라졌다.

몇 개의 버전을 더 받아서 확인해 본 결과 CentOS 5.7/RHEL 5.7의 2.6.18-274 커널에는 존재하지만 kernel.org에서 내려받은 2.6.35.13과 3.4 커널 코드에는 존재하지 않았다.

즉, 메모리 관리 기법에 대한 변화로 인해서 해당 코드는 삭제 된 것으로 추측된다. (2.6.20 커널의 패치 파일에서는 수식 변경이 발견되었고 그 뒤 버전에서는 아예 사라졌다) 변경된 내용에 대한 내용까지 조사해서 정리하면 좋겠지만 이 문서를 작성하면서 고려하지 않았던 내용이기 때문에 이 부분은 나중으로 미루려고 한다.

다만, 최신 커널 문서에서도 여전히 swappiness 값은 0과 100사이에서 동일한 의미로 설명되고 있기 때문에 설정 값에 대한 내부 처리 부분의 변경은 다시 확인해야겠지만 당장 설정하는데 별다른 문제는 없다.