top 명령어로 서버 정보를 볼 수 있다.
3 days: 현재 구동 된지 3일 정도 되었음.
PR: 프로세스 실행 우선순위
NI: PR을 얼마만큼 조정할 것인가
VIRT,RES,SHR: 프로세스가 사용하는 메모리의 양에 대한 것
VIRT: 할당된 가상메모리의 전체 크기.
RES: 그 중에 실재로 메모리에 올려서 사용하고 있는 물리 메모리의 크기 (그렇기 때문에 실제 메모리 점유율이 높은 프로세스를 찾기 위해서는 RES 영역이 높은 프로세스를 찾아야 한다)
SHR: 다른 프로세스와 공유하고 있는 메모리의 크기
S: 프로세스의 상태를 나타낸다.
VIRT는 malloc과 같은 시스템 콜로 필요로 하는 메모리 영역을 할당해 달라고 요청을 하면 커널은 프로세스가 사용할 수 있는 가상 메모리주소를 전달해준다. ⇒ Memory Commit
그 후 프로세스가 할당받은 메모리 영역에 실제로 쓰기 작업을 하면 Page fault가 발생하며 실제 물리 메모리에 프로세스의 가상 메모리 공간을 매핑한다.
한도 끝도 없이 늘어날까? → vm.overcommit_memory라는 파라미터로 그렇게 할 수 도있고 못하게 할 수도 있다.
왜 즉시 할당하지 않는 것일까? → fork()와 같은 새로운 프로세스를 만들기 위한 콜을 처리해야 하기 때문이다.
S - Process Status를 의미
D: uninterrubtable sleep 상태로 프로세스가 디스크 혹은 네트워크 작업을 하게 되면 요청이 도착할 때 까지 아무것도 할 수 없기 때문에 CPU에 대한 사용권을 다른 프로세스에게 넘기고 자신을 D로 마킹한다.
R: 실행중인 프로세스이기 때문에 실제 CPU 자원을 소모한다.
S: sleeping 상태의 프로세스
T: strace등으로 프로세스의 시스템 콜을 추적하고 있는 상태로 보통의 시스템에서는 자주 볼 수 없다.
Z: zombie상태의 프로세스로 부모 프로세스가 죽은 자식 프로세스를 의미한다.
D 상태의 프로세스가 많다면 특정 요청을 기다리고 있는 것이고 다시 R상태로 돌아가야 하기 때문에 시스템 부하를 계산하는데 포함된다.
Z 프로세스는 시스템의 리소스를 차지하지 않아서 그 자체는 문제가 되지 않는다. 하지만 pid를 점유하고 있기 때문에 새로운 프로세스에 pid를 할당하지 못하여 고갈이 되는 문제가 발생할 수 있다.
기본적으로 모든 프로세스는 20의 우선순위 값을 갖는다. 여기에 NI를 주면 우선순위값이 바뀌게 된다. 이 우선순위 값이 낮을 수록 더 빨리 실행된다.
Load Average는 얼마나 많은 프로세스가 실행 중 혹은 실행 대기 중이냐를 의미하는 수치이다. 이 값은 절대적이 아니라 상대적인데 계산과정이 시스템에 있는 CPU Core 수가 몇 개냐에 따라 달라지기 때문이다.
Kernel Timer가 주기적으로 프로세스의 개수를 센다.
Load Average가 높다는 것은 CPU를 사용하려는 프로세스가 많다는 것 뿐만 아니라 I/O쪽에 병목이 생겨서 I/O작업을 대기하는 프로세스가 많을 수도 있다는 이야기다. → 그렇기 때문에 Load Average가 높다고 단순히 CPU가 더 많은 장비를 사용하는 것으로 해결할 수 없다.
vmstatvmstat 명령어로 시스템의 상태를 확인해 볼 수 있다.
r: 실행되기를 기다리거나 현재 실행되고 있는 프로세스의 개수
b: I/O를 위해 대기열에 있는 프로세스의 개수
참고로 커널은 완벽하지 않기 때문에 버그가 있을 수 있으며 커널 버전이 달라지면 모니터링 지표가 제대로 수집되지 않을 가능성이 있다. 하나의 지표로만 모니터링하지말고 다양한 툴들과 지표를 조합해서 운영해야 한다.
CPU: 프로세스의 연산 과정에 필요한 리소스
메모리: 프로세스가 연산할 수 있는 공간을 제공해주는 리소스
이 메모리가 어떻게 활용되고 있는지를 파악하는 것은 CPU 사용률과 Load Average만큼 중요한 포인트이다.
free -mfree: 시스템에서 아직 사용하고 있지 않은 메모리의 양을 의미. 애플리케이션이 사용할 수도 있고 커널이 사용할 수도 있다.
buffers: 버퍼용도로 프로세스가 사용하는 영역은 아니고 시스템의 성능 향상을 위해서 커널이 사용하는 영역이다.
cached: 페이지 캐시라고 불리는 캐시 영역에 있는 메모리의 양을 의미한다. I/O관련 작업을 더 빠르게 진행하기 위해 커널에서 사용하고 있는 영역이다.
-/+ buffers/cache: 윗 줄에서 buffers와 cached를 제외한 양을 보여주는 것.
커널은 블록 디바이스라고 부르는 디스크로부터 데이터를 읽거나 사용자의 데이터를 디스크에 저장한다. 하지만 디스크는 다른 장치들에 비해 매우 느리기 때문에 시스템 부하가 일어나기도 한다. 커널은 디스크에 대한 요청을 빠르게 하기 위해 메모리의 일부를 디스크 요청에 대한 캐싱 영역으로 할당해서 사용한다. 한 번 읽은 디스크의 내용을 메모리에 저장해서 재요청시 디스크로 요청하지 않고 메모리로 요청한다.
추가 설명
cached: 커널이 읽어야 할 데이터가 파일의 내용일 때.
buffers: 파일의 내용이 아닌 파일 시스템을 관리하기 위한 메타 데이터를 읽어올 때.
서버의 운영 기간이 길지 않을 때는 가용영역이 많다가 시간이 흐를수록 차게된다. 사용영역이 점점 더 커져서 일정 수준 이상이 되면 캐시영역으로 사용하던 영역을 애플리케이션이 사용할 수 있도록 메모리 관리 시스템에 반환한다. 이 과정을 거치다보면 더 이상 가용할 메모리가 없는 순간이 발생할 때 swap이 일어난다.
/proc/meminfoSwapCached: swap으로 빠진 메모리 영역 중 다시 메모리로 돌아온 영역을 의미. 메모리가 확보되었지만 swap영역에서 해당 메모리 내용을 삭제하지 않는다. 추후 메모리 부족 상황을 대비하는 것. 그래서 swap이 다시 발생하더라도 조금이나마 I/O를 줄일 수 있다.
Active(anon): Page Cache영역을 제외한 메모리 영역을 의미한다. 주로 프로세스들이 사용하는 메모리영역을 지칭할 때 사용
Inactive(file): 커널이 I/O 성능 향상을 위해 사용하는 영역. buffers, cached 영역이 여기에 속한다.
커널 역시 프로세스의 일종이기 때문에 메모리를 필요로 한다.
Slab: 커널이 직접 사용하는 영역
Slab영역 중 가장 많이 사용되는 캐시가 dentr와 inode_cache이다. 각각은 디렉터리의 계층 관계를 저장한다. 파일에 자주 접근하고 디렉터리의 생성/삭제가 빈번한 시스템이 있다면 Salb 메모리가 높아질 수 있다.
간혹 프로세스들이 사용하는 메모리 영역을 모두 더하고도 used와 맞지 않을 경우 Slab 메모리에서 누수가 발생하는 것일 수 도 있다. 메모리의 동작이 이상하다고 생각 될 때는 /proc/meminfo를 통해서 좀 더 정확한 정보를 수집하는 것이 필요.
swap 영역은 물리 메모리가 부족할 경우 대비해서 만들어 놓은 영역이다. swap 영역은 물리 메모리가 아니라 디스크이 일부분을 메모리 처럼 사용하기 위해 만든 것이므로 속도가 현저하게 떨어질 수 밖에 없다.
swap 영역을 사용했다는 것 자체가 시스템 메모리와 관련해서 문제가 있다는 것.
추가적으로 누가 swap을 사용하느냐도 매우 중요한 판단의 기준이 된다.
모든 프로세스는 /proc/의 디렉터리에서 자신과 관련된 정보들을 저장한다.
/proc/<pid>/smaps다음과 같이 각 프로세스 별로 메모리 정보를 저장하고 있는 곳을 볼 수 있다.
너무 많아서 불편하다면
/proc/<pid>/status를 통해서 전체 swap 영역에 대한 정보를 볼 수 있다.
전체 프로세스 별로 사용을 하려면 smem유틸리티를 사용하면 된다.
smem -t커널은 버디 시스템을 통해서 프로세스에 메모리를 할당한다. 버디 시스템은 물리 메모리를 연속된 메모리 영역으로 관리를 한다. 그렇기 때문에 메모리의 단편화를 막을 수 있다.
/proc/buddyinfo커널은 메모리의 요청이 발생 했을 때 시스템에서 가장 적당한 버디 리스트를 찾아 프로세스에 넘겨준다.
- 커널이 사용하는 캐시메모리의 재할당
캐시 용도로 사용하던 메모리를 사용 해제 하고 가용 메모리 영역으로 돌려서 프로세스가 다시 사용할 수 있게 해주는 것. (커널은 메모리가 안 쓰이고 가용상태로 남아있는걸 좋아하지 않기 떄문에 프로세스가 사용하고 있지 않은 가용메모리를 본인이 캐시 용도로 써버린다. 그렇기 때문에 실제 프로세스가 사용한다고 했을 때 다시 줘야하기 떄문에 재할당을 한다.)
- swap을 사용하는 재할당
캐시 용도의 메모리를 해제하고도 더 이상 프로세스에게 줄 수 없을 때 swap을 사용한다. 디스크 I/O가 일어나기 때문에 성능저하가 일어난다.
vmstat명령으로 메모리 할당 변화를 초 단위로 볼 수 있다.
vm.swappiness , vm.vfs_cache_pressurevm.swappiness: 메모리 부족 상황에서 캐시를 비우느냐, 특정 프로세스의 메모리 영역을 swap으로 옮기느냐 결정.
왜 이런게 있을까? 무조건적인 페이지 캐시 해제가 항상 좋은 것만은 아니기 때문이다. 페이지 캐시는 I/O 작업 시 디스크로의 접근을 줄여주기 때문에 전체적인 응답 속도를 향상시킬 수 있기 때문이다. 그렇기 때문에 커널은 사용자에게 선택권을 준다.
vm.vfs_cache_pressure: 위의 설정으로 캐시를 재할당 한다고 결정됐을 경우 Page 캐시를 더 많이 재할당할지 디렉터리나 inode캐시를 더 많이 재할당할지 결정한다.
단순히 메모리를 증설한다고 해서 문제가 해결되지 않는다. 앱 자체에서 메모리 누수가 있다면 발현시간만 늦출 뿐이다.
메모리 사용량 그래프를 보았을 때
- 지속적으로 증가하는 추세: 보통 메모리 누수를 의심해 볼 수 있다. (pmap 명령어나 gdb같은 도구를 이용하여 힙 메모리 영역의 메모리 덤프를 생성해서 실제 어떤 데이터들이 메모리에 있는지 확인한다)
- 순간적으로 폭증하는 경우: 메모리를 증설하면 도움이 되지만 이런 경우는 흔치 않고 서비스에 영향 끼칠 정도가 아니라면 swap을 사용하는 것으로 방어하는 것도 하나의 방법이 될 수 있다.


