[실습과 그림으로 배우는 리눅스 구조] - free

이 글은 [실습과 그림으로 배우는 리눅스 구조] - free에서 이어졌습니다.

이번에는 책을 바탕으로 메모리 관련 부분에 대해서 정리해보았다. 리눅스에서는 free 명령어를 통해서 현재 서버의 메모리 상황을 볼 수 있다. [free 명령어 예시]

[root@host1 ~]# free
                   total         used            free     shared    buffers     cached
Mem:        126060     123476       2584          0      41396      47364
-/+ buffers/cache:      34716      91344
Swap:       524152       1148     523004

[참고] 기본은 Byte이다. free -m, free -g를 통해 Megabyte, Gigabyte단위로 확인이 가능하다.
위와 같이 free 명령어를 날려보면 total, used, free, shared, buffers, cached, swap 까지 디테일한 메모리 정보를 알 수 있다. total, used, free까지는 직관적으로 이해가 되는데, shared, buffers/cached, swap는 명확히 알고있지 못했다. 이 부분에 대해서 역시 책을 바탕으로 아래에서 정리해 볼 예정이다.

Linux에서의 메모리

Linux 서버에서의 전체 메모리는 커널 메모리 + (프로세스의 메모리) * N + 빈 메모리로 표현할 수 있다. 커널 메모리는 리눅스 OS 자체에서 사용하는 메모리, 프로세스는 리눅스 위에서 동작하는 프로그램이 사용하는 메모리, 그리고 이 둘을 제외한 부분이 빈 메모리라고 할 수 있다. 각 부분을 위의 free 명령어에 매핑해보면 아래와 같다.

total

전체 메모리이다. 위 예시에서 전체 126060Byte, 12GB임을 알 수 있다.

free

전체영역 중 아예 사용되지 않는 메모리이다.

buff/chache

버퍼 캐시, 페이지 캐시에 이용되는 메모리이다. 시스템의 빈 메모리=free 영역이 부족하면 커널이 해제를 요청하고 버퍼/캐시된 부분을 free 영역으로 반환하여 사용이 가능하다.

available

실질적으로 사용이 가능한 메모리이다. free부분이 부족하면 buff/cache 영역 또는 커널메모리의 일부가 해제되며 사용 가능한 영역이 되기 때문에 이 부분까지를 availble 영역으로 본다.

swap

물리메모리에 메모리 할당을 요청하였는데 부족할 경우 이 부분을 사용한다. 스왑에 관해서는 아래에서 더 자세히 설명하겠다.



결론은, 빠른 판단이 필요할 때에는 free -m 명령어에서 available(또는 free + buff/chace)를 보는 것이다.

메모리 할당 과정

이 책에서는 메모리 할당 과정에 필요한 기술들을 순차대로 설명하고 있다. A라는 기술은 사실 C라는 기술로 이루어져 있는데 C를 설명하기 위해서는 B를 이해해야하기 때문에 설명은 B - C - A 순서로 전개된다.

프로세스는 커널을 통해서 메모리를 할당받는다. 커널은 프로세스 생성 시와 프로세스 생성 후 프로세스가 추가 메모리 할당을 요청하면 동적으로 할당해준다. 커널은 메모리 확보용 시스템 콜을 통해 프로세스에게 빈 메모리 일부의 시작주소값을 반환하게 되는데, 이 경우에 여러 단점이 생긴다. 이를 테면, 1~1024까지 사용가능했는데, 10~300, 400~600, 700~900, 950~1024 이런 방식으로 할당이 되어있는 상태에서, 전체적으로는 10+100+100+50=260 정도가 남지만 정작 260만큼을 요청해도 메모리 사용 공간들이 쪼개져버려 사용할 수 없게 된다. 이런 현상을 메모리단편화 라고 한다.(방금 설명은 매우 간략히 설명한 것으로, 자세한 설명은 위의 글에 매우 자세히 설명되어 있습니다.) 이런 단점을 해결하기 위해 커널은 ‘가상메모리’를 사용한다.

가상메모리

시스템 메모리의 실제주소(real address) = 물리주소(physical address)에 프로세스가 직접 접근하지 않고 논리주소(logical address) = 가상주소(virtual address)를 이용하여 간접적으로 메모리에 접근하는 방식이다. 이 때 가상주소와 물리주소가 상호 매핑되어 있는 표를 ‘페이지 테이블’이라고 하고, 가상주소와 페이지 테이블 등을 통틀어 가상메모리 방식이라고 일컫는다.
책을 보기 전까지는 swap 메모리가 가상메모리와 동일하다고 알고 있었다. swap 메모리는 물리적으로 RAM과 동일한 메모리가 아니기 때문에 가상메모리의 Concept인 것은 맞으나, 여기서 설명하고자 하는 가상메모리의 의미와는 다르다.
프로세스는 프로그램의 실행파일을 통해 프로그램 보조정보를 읽어들이고 페이지테이블을 작성하여 가상주소와 물리주소를 매핑하게 된다. 페이지테이블을 통해 물리주소는 불연속적이지만 이를 가상주소 상에는 연속적으로 매핑할 수 있다. 즉, 메모리 단편화 문제를 해결할 수 있다.

가상메모리의 응용

가상메모리는 여러 방법으로 응용 구현 되어있다.

파일맵

DISK에 저장되어 있는 파일은 읽고 쓰는 작업이 메모리보다 오래 걸린다. 이를 개선하기 위해 프로세스는 파일에 접근할 때 파일을 연 뒤 read(), write(), lseek() 등의 시스템 콜을 사용하는데, 이 시스템 콜들은 mmap()을 통해 파일의 내용을 메모리로 읽어들이고 가상주소 공간에 매핑한다. 매핑한 파일은 메모리 접근과 동일하게 가상메모리의 주소를 통해 접근할 수 있다.

디맨드 페이징

프로세스에 메모리 할당 시에 커널이 필요한 영역을 메모리에 확보한 뒤 가상공간에 물리공간을 바로 매핑하지 않고, 가상공간에 대한 메모리 요청이 들어오면 page fault를 발생시켜 그 때 물리메모리 공간을 매핑한다. 즉, 요청이 들어오면 그 때 매핑하는 lazy한 방식이다.

스왑

어떠한 방식이든 물리메모리에 요청했는데 메모리가 부족할 경우에 발생한다. 커널은 프로세스들이 사용하고 있던 메모리 영역 중 가장 사용하지 않을 것 같은 프로세스의 메모리를 스왑영역(주로 저장장치가 스왑영역으로 설정된다.)으로 옮긴다. 이를 swap out(page out) 이라 한다. 시간이 흘러 물리메모리에 여유가 생기면 스왑영역의 메모리를 다시 물리 메모리로 옮기는 swap in(page in)과정이 일어난다. swap in, swap out과정을 swapping(paging) 이라고 한다.
리눅스에서 스왑의 단위가 페이지이기 때문에 paging 이라고도 한다. 간혹 메모리에 접근할 때마다 swap in, swap out이 잦게 발생하는 경우가 있는데 이를 thrashing상태라고 하며, 물리메모리를 늘리는 것이 필요하다. free 명영어로는 실시간으로 swap이 일어나는것을 모니터링하기는 어려운데, top명령어의 상단 제일 아랫부분에서 Swap 모니터링이 가능하다.

캐시

지금까지 이야기한 메모리는 우리가 흔히 생각하는 메모리인 RAM이다. 그런데 컴퓨터에는 RAM말고도 데이터를 저장하는 다양한 장치가 있다. 이를 메모리 장치의 계층으로 표현하는데 작고, 비싸고, 빠른 순서로 레지스터 > 캐시메모리 > 메모리 > 저장장치이다. 레지스터와 캐시메모리는 CPU영역이 속하고, 저장장치는 DISK 영역이다.

캐시 메모리

컴퓨터는 크게 다음과 같은 세 가지 처리 흐름을 갖고있다. 1) 명령어를 바탕으로 메모리에서 레지스터로 데이터 읽기 2) 레지스터 데이터를 바탕으로 계산 3) 계산 결과를 메모리에 씀
CPU 작업에 있어서 중요도는 2)번이지만, 실제로는 1)과 3)에서 매우 오래걸린다고 한다. 병목인 것이다. 그럴 수 밖에 없는것이 CPU 바깥에 있는 메모리로부터 데이터를 읽어오는 과정이 단연 CPU 내부에서의 동작보다 시간이 오래걸린다. 어딘가에서 가져오는 작업이니 오래걸린다는 것이다. 이처럼 Memory에 찾아갔다 오는 시간을 줄여 latency를 줄이고자 CPU 내부에도 작은 메모리가 있다. 이를 캐시메모리라고한다. 일반적으로는 CPU내부에 있는데, CPU밖에 있는 캐시메모리도 있다고 한다. 캐시메모리는 L1, L2,… 와 같이 레벨로 표현되는데 낮은 레벨일 수록 레지스터에 가깝고 빠르고 용량이 작다.

페이지 캐시

CPU에서 메모리 접근한 속도도 느리다고 하는데, 저장장치에 접근하는 속도는 더 빠를 리가 없다. 역시 CPU가 저장장치에 접근해 데이터를 읽어오는 속도를 높이기 위해 ‘페이지 캐시’라는 기능을 사용한다. 캐시메모리와 비슷하게 저장장치 내의 파일데이터를 메모리에 캐싱하는 것이다. 페이지 캐시 영역은 공유영역으로 모든 프로세스가 접근 가능하다. 또 캐시 사이즈는 시스템 메모리가 비어있는 한, 프로세스가 페이지 캐시에 없는 파일을 읽을 때마다 증가한다. 시스템 메모리가 부족해지면 커널이 페이지캐시를 해제하여 메로리를 반납할 수 있도록 한다.




이렇게 리눅스에서 메모리 모니터링 방법과 동작 원리를 정리해보았다. OS의 동작 방식이 아주 쉽고 자세히 설명되어있는 좋은 책이다. 완벽히 이해가 안 간 부분도 있었는데, 궁금할 때마다 다시 한번 두고 두고 챙겨보고 싶은 책이다.
나처럼 CPU, Memory, Disk 등 OS가 어떻게 동작하는지 좀 더 자세히 (그렇지만 공룡책처럼만큼은 아닌) 알고싶은 사람들에게 꼭 읽어보라고 하고싶다.