킴의 레포지토리

🔍 운영체제 메모리 관리에 대해서 알아보고 터미널에서 시스템 사용 상황 모니터링 해보기 본문

study/cs

🔍 운영체제 메모리 관리에 대해서 알아보고 터미널에서 시스템 사용 상황 모니터링 해보기

킴벌리- 2024. 5. 21. 19:09

이 글은 운영체제의 메모리 관리에 대해서 알아보고 실습한다. 먼저 1. 프로세스의 메모리 영역에서 운영체제가 프로세스에 할당하는 메모리를 어떻게 구분해서 관리하는지 알아본다. 다음으로 2. 가상 메모리에서 운영체제 메모리 관리의 핵심인 가상 메모리에 대해서 알아본다. 가상 메모리란 무엇이고, 왜 사용하는지 알아본다. 그리고 3. 가상 메모리의 응용에서 가상 메모리 기술을 응용하여 요구페이징과 스와핑이 어떻게 메모리를 효율적으로 사용하게 하는지 알아본다. 마지막으로 4.모니터링 실습에서 macOS 컴퓨터의 터미널에서 시스템 사용 상황을 모니터링 해본다.

1. 프로세스의 메모리 영역

프로그램을 실행하면 운영체제가 프로세스를 생성하고 메모리를 할당한다. 운영체제는 프로세스의 메모리를 코드, 데이터, 스택, 힙 영역으로 구분하여 관리한다. 각 메모리 영역은 특정한 용도를 가지며 실행시 메모리 크기 변경 여부, 접근 권한이 다르다.

 

- 코드(텍스트)영역: 프로그램 실행 코드가 저장되는 영역. 쓰기가 불가능한 영역으로 프로그램 시작시 할당된 후 프로그램이 실행되는 동안 변경되지 않는다. 프로세스가 실수로나 악의적으로 자신의 실행코드를 변경하는 것을 방지한다.

- 데이터: 전역 변수와 정적 변수가 저장되는 영역으로, 프로그램 시작시 할당된 후 프로그램 생명주기동안 데이터 영역의 정보는 계속 유지됨. 크기는 고정되지만 값은 변경 가능하다.

- 스택 : 스레드 별로 함수 호출과 로컬 변수 저장에 사용된다. LIFO 구조로 관리된다. 운영체제에 의해 스레드별 최대 스택 사이즈가 정해진다(ulimit 명령어 참고 - 아래에서 설명). 함수호출과 함께 자동으로 확장된다. 잘못된 재귀 함수로 인해 최대 스택 사이즈를 넘어가도록 함수 호출히 일어나는 경우 stack over flow가 발생한다.

- 힙: 동적 메모리 할당에 사용된다. 프로그램 실행 중에 메모리를 운영체제로부터 할당받거나 해제할 수 있다.

 

  코드 데이터 스택
실행 중 메모리 크기 변경 여부 컴파일 시 크기 결정됨 → 프로그램 시작시 할당받은 메모리만 사용 컴파일 시 크기 결정됨 → 프로그램 시작시 할당받은 메모리만 사용 실행 중 프로그램 요청에 따라 메모리 할당/해제 가능 함수 호출과 함께 자동으로 확장. 함수 반환시 해제
접근 권한- 읽기 O O O O
접근 권한- 쓰기 X O O O
접근 권한- 실행 O X X X

 

✔️ 스택메모리는 가장 높은 부분에서 시작해서 아래로 확장하고, 힙 영역은 스택 영역과 간격을 두고 시작해서 높은 주소 공간으로 확장해간다. 스택과 힙 사이의 간격은 두 영역이 확장될때 충돌을 방지하는 버퍼 역할을 하기 때문에 상당히 큰 빈 공간을 둔다.

✔️메모리 별 접근 권한은 운영체제의 커널이 사용하는 메모리 내 페이지 테이블에서 관리한다. 페이지 테이블에 대해서는 아래에서 설명한다.

2. 가상 메모리

가상 메모리란 프로세스가 메모리에 직접 접근하지 않고 가상 주소(논리 주소)를 사용해서 간접적으로 접근하도록 하는 방식이다. 운영체제는 프로세스에 독립적인 가상 주소 공간을 할당하고 프로세스는 가상 주소 공간을 사용한다. 운영체제는 가상 주소 공간을 작은 블록 단위인 페이지로 나누어서 실제 메모리에 매핑한다. 즉, 가상 메모리란 불연속적인 물리적 메모리를 연속적인 논리 메모리로 추상화해서 프로세스가 사용할 수 있게 하는 메모리 기법이다.

2-1. 가상 메모리를 사용하는 이유

프로세스가 메모리의 물리 주소를 사용해 직접 메모리에 접근하는 경우 다음과 같은 문제가 발생한다.

 

1. 메모리 보호 부족: 잘못된 프로그래밍으로 다른 프로세스의 메모리에 접근해 읽고 쓸 수 있다.

2. 프로세스간 메모리 충돌: 프로세스 간의 메모리 충돌을 방지하기 위해 메모리를 수동으로 관리해야한다. 프로그램 작성시 메모리 할당 및 해제 작업이 복잡해진다. 다른 프로세스의 메모리 사용에 영향을 받는다.

3. 메모리 단편화: 사용 가능한 메모리가 작은 조각으로 나뉘어져 큰 메모리 블록을 할당할 수 없게 되어 시스템 성능이 저하된다.

4. 프로세스가 물리 메모리의 특정 위치에 종속: 메모리의 어느 위치에 로드되는지 고정되어 다양한 환경에서 실행할 수 없다.

 

가상 메모리는 이러한 문제들을 해결하기 위해 설계되었다.

 

1. 메모리 보호 : 각 프로세스 별로 독립된 가상 주소 공간을 가지기 때문에 다른 프로세스의 메모리에 접근이 불가능하다.

2. 프로세스 간 메모리 충돌 방지: 각 프로세스 별로 독립된 가상 주소 공간을 가지기 때문에 프로세스 간 메모리 충돌 가능성이 없다. 프로그램 작성시 프로세스 간 메모리 충돌을 신경쓸 필요가 없다.

3. 메모리 단편화 해결: 프로세스는 가상 주소 공간을 큰 하나의 공간처럼 사용하지만 실제 운영체제는 메모리를 페이지로 나누어 관리한다. 큰 연속적인 메모리 블록이 필요하지 않기 때문에 메모리 단편화가 발생하지 않는다. 메모리를 효율적으로 사용할 수 있게 된다.

4. 프로세스 이식성 향상: 프로그램은 실제 물리 메모리의 어디를 사용하는지 신경쓰지 않고, 전체 메모리를 하나의 큰 영역으로 간주하고 사용한다. 물리 메모리에 종속되지 않기 때문에 다양한 환경에서 실행될 수 있다.

 

✔️ 가상 메모리 관리는 투명하게(transparent)하게 이뤄지므로 프로그램은 자신이 물리 메모리에 직접 접근하는 것처럼 동작한다.

2-2. 페이징과 페이지 테이블

운영체제는 프로세스의 가상 주소 공간을 페이지로 나누어 관리하고, 페이지별로 실제 물리 메모리를 매핑한다. 운영체제가 사용하는 커널 메모리에는 페이지 테이블이 있어서 프로세스 별 가상 주소 공간과 매핑된 실제 물리 메모리 정보를 관리한다. 페이지 테이블은 프로세스 별로 생성된다.

프로세스 별 페이지 테이블에는 가상 주소별로 페이지 테이블 엔트리가 존재한다. 페이지 테이블 엔트리의 주요 필드는 다음과 같다. 각 페이지 테이블 엔트리는 물리 주소와 기타 메타데이터를 포함한다.

  역할 참고
물리주소 가상 페이지가 매핑된 물리 페이지 주소. 페이지는 가상 메모리 주소 공간을 나눈 고정 크기 블록이고, 프레임은 물리 메모리 주소 공간을 나눈 고정 크기 블록이다. 가상 페이지는 물리 프레임에 매핑된다.
유효 비트
(valid bit)
페이지가 실제로 물리 메모리에 존재하는지 여부.
존재한다면 1 존재하지 않으면 0
0인 경우, 페이지가 메모리에 매핑되지 않았거나 디스크에 스왑 아웃된 상태이다. MMU가 페이지 폴트 판단시 사용한다.
페이지 접근 비트 페이지가 최근에 접근되었는지 여부. 접근하면 1 아니면 0. 페이지 교체를 위한 Clock Algorithm에서 사용된다 (아래에서 설명)
읽기/쓰기 비트 페이지에 대한 접근 권한. 1이면 읽기/쓰기 가능, 0이면 읽기 전용  
스왑 엔트리 스왑 영역과, 스왑 파일에서 페이지의 위치를 나타내는 정보로 구성 페이지가 스왑 아웃되었을때, 다시 메모리로 로드하기 위해서 사용됨(아래에서 설명)

2-3. MMU(Memory Management Unit)의 역할

CPU가 프로세스를 처리하면서 가상 주소를 생성하면 MMU라는 하드웨어 장치가 페이지 테이블을 사용해 가상 주소 공간을 물리 주소로 변환하여 CPU가 실제 메모리에 접근할 수 있도록 한다. 가상 주소 공간을 물리 주소로 변환하는 과정에서 MMU는 다음과 같은 과정을 거친다.

1. MMU는 먼저 TLB(Translation Lookaside Buffer)조회하여 캐싱된 매핑 정보가 있는지 확인한다. TLB 히트시 바로 물리 주소를 반환한다. TLB 미스시 페이지 테이블을 조회한다.

2. 가상 주소로 매핑된 페이지 테이블 엔트리를 찾는다.

3.페이지의 메타데이터를 사용해 유효한 메모리 접근인지 확인한다. - 유효 비트를 사용해 페이지가 물리 메모리에 있는지 검사한다. 없다면 페이지 폴트 인터럽트를 발생시킨다. - 읽기 쓰기 비트를 사용해 권한을 검사한다. 페이지 권한을 벗어나는 접근 요청의 경우 접근위반(access violation) 페이지 폴트 인터럽트를 발생시킨다.

4.페이지 접근 비트를 업데이트 하고 TLB에 캐시한다.

5.가상 주소를 반환한다.

[참고] TLB는 MMU 내의 고속 캐시로 최근 사용된 가상 주소와 물리 주소의 매핑을 저장해, 물리 주소 조회 속도를 높인다. TLB 히트시 페이지 테이블을 참조하지 않고도 빠르게 주소 변환을 제공한다

3. 가상 메모리의 응용

운영체제는 물리 메모리를 직접 사용하지 않고 추상화한 후 페이지로 나누어서 관리하는 가상 메모리 기법에 더해 요구 페이징과 스와핑을 사용해서 더욱 메모리를 효율적으로 사용한다.

3-1. 요구 페이징(demand paging)

3-1-1. 요구페이징이란?

프로세스가 실제로 필요로 할 때까지 페이지를 물리 메모리에 로드하지 않는 기법이다. 사용하지 않을 코드와 데이터를 모두 물리 메모리에 로드하는 것은 메모리를 낭비하게 된다.요구 페이징을 사용할 경우 실제 필요한 페이지만 메모리에 로드하므로 메모리 사용량을 줄일 수 있다.이러한 문제를 해결하기 위해 요구 페이징 방식은 가상 주소 공간 내의 페이지에 처음 접근할때 실제 물리 메모리를 매핑한다. 페이지는 접근하기 전까지 물리 메모리에 할당되지 않고 가상 메모리 상에만 존재한다. 즉, 페이지는 처음 CPU에 의해서 참조되기 전까지 ‘프로세스에는 할당되었지만 물리 메모리에는 할당되지 않은’ 상태로 존재한다.

3-1-2. 메모리 영역에 따른 요구 페이징 동작 방식

프로세스 메모리의 각 영역에 대해 요구페이징은 다음과 같은 방식으로 동작한다.

 

- 코드 영역: 프로그램의 실행코드 전체를 디스크에서 메모리로 로드하는 것이 아니라, 가상 주소 공간의 실행 코드가 CPU에 의해서 실행될때 디스크에서 실행코드를 매핑된 메모리로 로드한다. 예를 들어, 특정 함수가 처음 호출될때, 해당 함수가 위치한 페이지를 디스크에서 메모리로 로드한다.

- 데이터 영역: 전역 변수와 정적 변수가 처음으로 참조될때 요구 페이징에 가상 주소 공간이 실제 메모리에 매핑되고, 디스크에서 값을 로드한다.

- 힙 영역: 동적 메모리 할당 시점이 아니라, 할당된 힙 페이지가 실제로 초기화될때 할당된 가상 공간을 실제 메모리에 매핑한다. 메모리를 할당 받은 후, 해당 메모리 공간에 데이터를 쓰거나 읽으려고 할때 페이지가 메모리에 로드된다.

- 스택 영역: 스레드 생성이나 함수 호출로 스택 영역이 확장될때, 확장된 가상 주소 공간을 물리 메모리에 매핑한다.

3-1-3. 요구 페이징에 의해 페이지 폴트가 발생하면 생기는 일

CPU가 ‘프로세스에 할당되었지만 물리 메모리에 할당되지 않은 페이지’에 접근하면 다음과 같은 일이 일어난다.

1. MMU는 페이지 폴트 인터럽를 발생시킨다.

2. 페이지 폴트가 발생하면 프로세스는 중단된다. 프로세스는 필요한 페이지가 로드될 BLOCKED 상태로 대기한다.

3. CPU는 커널모드로 전환되어 ISR(interrupt service routine)을 실행한다. 이때 페이지 폴트 핸들러가 호출된다.

4. 페이지 폴트 핸들러는 필요한 페이지를 디스크에서 메모리로 로드하거나, 물리 메모리 중 비어있는 프레임을 가상 페이지와 매핑하는 런타임 바인딩을 실행한다.

5. 페이지 폴트 핸들러가 완료되면 페이지 테이블이 업데이트 되고, 프로세스는 Ready 상태로 전환된다.

코드 및 데이터 영역에 요구 페이징에 의한 페이지 폴트가 발생하면 디스크에서 메모리로 페이지를 로드하는 디스크 I/O 작업이 일어난다. 반면에 힙 및 스택 영역에 요구 페이징에 의한 페이지 폴트가 발생하면 런타임 페이징이 실행된다.

3-2. 스왑

3-2-1. 스왑이란?

운영체제는 페이지를 해제하지 않은 채, 빈 프레임을 최대한 많은 가상 페이지에 매핑한다. 운영체제는 요구 페이징에 의해서 한번 매핑된 물리 메모리를 가능하면 유지한다. 최근에 접근한 페이지는 또 다시 접근할 가능성이 많고, 이때 페이지 폴트가 다시 발생하는 것을 방지하고 성능을 최적화하기 위함이다.

[참고] 프로그램의 메모리 접근 패턴 (Locality)

- 공간 지역성: 인접한 정보라면 곧 사용될 가능성이 높다. 공간 지역성때문에 운영체제는 메모리 공간을 페이지(블록)단위로 관리한다.
- 시간 지역성: 최근에 사용된 정보가 곧 다시 사용될 가능성이 크다. 한번 사용된 페이지는 곧 다시 사용될 가능성이 높으므로 해제하지 않고 메모리에 남겨둔다.

 

메모리가 부족해지면 운영체제는 자주 사용되지 않는 페이지를 디스크의 스왑 공간으로 이동시켜 빈 프레임을 확보한다(스왑 아웃). cpu가 스왑 영역에 있는 페이지를 참조할때 다시 스왑 영역에서 빈 프레임으로 로드한다(스왑 인). 즉, 디스크의 스왑 영역을 메모리로 간주해서 물리 메모리 부족을 해소한다. 이를 스왑이라고 한다. 스와핑을 사용하면 물리 메모리와 디스크 스왑 사이즈를 합친 크기를 메모리처럼 사용할 수 있다. 따라서 더 많은 프로세스를 동시에 실행할 수 있다.

3-2-2. 스왑에 의해 페이지 폴트가 발생하면 생기는 일

CPU가 스왑 영역에 있는 페이지에 접근하면 다음과 같은 일이 일어난다.

1. MMU는 페이지 폴트 인터럽를 발생시킨다.

2. 페이지 폴트가 발생하면 프로세스는 중단된다. 프로세스는 필요한 페이지가 로드될 BLOCKED 상태로 대기한다.

3. CPU는 커널모드로 전환되어 ISR(interrupt service routine)을 실행한다. 이때 페이지 폴트 핸들러가 호출된다.

4. 페이지 폴트 핸들러는 메모리의 빈 프레임을 찾는다. 빈 프레임이 없다면 페이지 교체 알고리즘을 사용하여 교체할 페이지를 찾는다. 교체할 페이지의 데이터를 디스크로 옮긴다(스왑 아웃)

5. 페이지 테이블에서 스왑 파일에서 불러올 페이지 위치를 찾은후, 스왑 영역에서 메모리로 로드한다. (스왑 인)

6. 페이지 폴트 핸들러가 완료되면 페이지 테이블이 업데이트 되고, 프로세스는 Ready 상태로 전환된다.

스왑인과 스왑아웃이 발생할때 메모리와 디스크 사이의 데이터 전송 즉, 디스크 I/O가 발생한다. 디스크 I/O는 오래 걸리는 작업이고, 비동기적으로 실행되기 때문에 디스크 I/O가 실행되는 동안 CPU는 다른 프로세스를 처리한다.

 

[참고] 페이지 교체 알고리즘
페이지 교체 알고리즘에는 LRU(Least Recently Used), Clock Algorithm, FIFO, LFU, MQ 등이 있다. 이 중 LRU, Clock 알고리즘은 시간 지역성을 바탕으로 최근에 사용된 페이지를 오래 메모리에 유지하기 위한 알고리즘이다. 이때 페이지 테이블의 페이지 접근 비트를 사용해서 페이지의 참조 여부를 추적한다.

3-2-3. 디스크 I/O 작업 중 CPU가 다른 프로세스를 처리할 수 있는 이유

디스크에서 필요한 데이터를 가져와서 메모리에 올리는 작업은 무거운 작업이다. 디스크는 메모리보다 탐색속도와 전송속도가 매우 느리기 때문이다. 디스크는 메모리보다 탐색속도가 10만배에서 100만배 정도 느리다. 메모리는 Random Access Memory라는 이름처럼 탐색 시 메모리 번지로 O(1)시간에 해당 주소에 접근이 가능하다. 하지만 디스크는 HHD의 경우 헤드의 이동과 원반의 회전이라는 물리적인 이동이 필요하다. SSD의 경우 전자적으로 데이터에 랜덤 접근하기 때문에 HDD보다는 훨씬 빠르지만 여전히 RAM보다 여전히 느리다. 또한, CPU와 메모리 사이의 버스 전송 속도보다 메모리와 디스크 사이의 전송 속도가 100배정도 느리다.

 

운영체제는 디스크 I/O 작업을 비동기적으로 처리한다. 페이지 폴트가 발생하여 디스크 I/O가 필요하다면, 운영체제는 이 요청을 디스크에 전달한 후 데이터가 준비될때까지 기다리지 않고 CPU를 반납한다. CPU는 디스크 I/O가 실행되는 동안 다른 프로세스의 명령을 실행한다. I/O 작업이 완료되면 디스크가 운영체제에 인터럽트를 보내어 처리할 수 있도록 한다. 이때 대기중인 프로세스는 Uninterruptible Sleep 상태로 프로세스가 디스크 I/O 작업 중에 중단되지 않도록 한다.

 

운영체제가 디스크 I/O작업을 비동기적으로 수행하고, CPU가 I/O 작업 동안 다른 작업을 할 수 있는 주된 이유는 Direct Memory Access(DMA) 기술 덕분이다. 이 기술은 메모리와 디스크 간의 데이터 전송을 CPU의 직접적인 개입 없이 자동으로 처리할 수 있게 한다.

디스크 I/O가 비동기적으로 일어나서 CPU가 I/O 작업에 독점되는 것은 아니지만, 여전히 페이지 폴트에 의한 I/O 작업은 발생하지 않는 것이 좋다. 페이지 폴트가 발생하면 운영체제가 디스크 I/O 작업을 요청하고 처리하기 위해 CPU 시간을 사용하게 되어 다른 사용자 프로세스들이 실행되지 못하고 CPU 처리 성능이 떨어질 수 있다. 또한, 페이지 폴트가 발생한 프로세스는 디스크 I/O를 기다려야하기 때문에 응답이 지연된다. 따라서, 스왑이 많이 발생하지 않도록 메모리 여유를 여유를 유지하는 것이 좋다.

[참고] 동기와 비동기, 블로킹과 논 블로킹
동기/비동기와 블로킹/논블로킹은 프로세스나 스레드가 다른 프로세스나 스레드에 작업을 요청할때의 행동방식을 설명한다.동기는 작업을 요청한 후 그 작업이 완료될 때까지 기다렸다가 결과를 전달받고, 비동기 방식은 작업을 요청한 후 그 작업이 완료될 때까지 기다리지 않고, 다른 작업을 수행하는 방식을 말한다. 작업이 완료되면 콜백 함수나 이벤트를 발생시킨다. 블로킹은 작업이 완료될때까지 대기하는 방식이고 논블로킹은 작업을 요청한 후 바로 제어권을 전달받아 자신의 다음 코드를 실행하는 것을 말한다.

일반적으로 동기 작업은 블로킹이 발생하고, 비동기 방식은 논블로킹이 발생한다. 동기는 정의에 의해 논블로킹일 . 수없고 비동기 요청이 블로킹으로 동작하는 경우 잘못된 프로그래밍일 가능성이 높다. (의견) 운영 체제의 페이지 교체를 의한 디스크 I/O 요청은 비동기 요청, 논블로킹 요청이고, 프로세스의 페이지 요청은 동기, 블로킹 요청으로 볼 수 있다. 가상 주소에 매핑된 물리 페이지를 찾을때까지 프로세스가 진행될 수 없기 때문이다.

 

3-2-4. 스레싱

스레싱(Thrashing)은 시스템이 과도한 페이지 교체로 인해 대부분의 시간을 디스크 I/O 작업에 소비하면서 실제 처리 성능이 급격히 저하되는 상태를 말합니다.

 

원인

시스템에 설치된 메모리 사이즈에 비해 과도하게 멀티태스킹을 할때 발생한다. 동시에 실행되는 프로세스가 너무 많을 경우 프로세스당 할당된 물리 메모리 공간은 줄어들고 많은 페이지들이 스왑 영역에 저장된다. CPU가 프로세스를 실행할때 필요한 페이지가 물리 메모리보다 스왑 영역에 있는 경우가 많아 페이지 폴트가 많이 발생한다. 페이지 교체를 위해 프로세스가 자주 중단되고 CPU 이용률이 낮아진다. 운영체제는 CPU 이용률을 높이기 위해 더 많은 프로세스를 실행하고 이로 인해 물리 메모리와 스왑 공간 모두 부족한 상태가 될 수 있다. 스레싱이 해소되지 아 OOM(Out of Memory)상태로 판단될 경우 운영체제는 중요도가 낮은 프로세스가 강제로 종료할 수 있다.

 

CPU의 스레싱 방지

CPU는 스레싱을 방지하기 위해서 워킹 세트(working set)와 PFF(page fault frequence) 알고리즘을 사용해서 페이지 교체를 최적화한다. 워킹 세트란 특정 시간 동안 프로세스가 실행되는데 필요한 페이지의 집합을 의미한다. 운영체제는 프로세스의 실행 패턴을 추적해서 워킹 세트를 구성한다. 페이지 폴트가 발생하면 페이지 접근 비트가 0이고, 워킹 세트에 포함되지 않은 페이지를 교체한다. 즉, 워킹 세트란 메모리 접근 패턴의 시간 지역성을 바탕으로 특정 시간 동안 프로세스가 자주 참조하는 페이지의 집합을 메모리에 유지하는 것이다. PFF란 페이지 폴트 빈도를 모니터링하여 워킹 세트 크기를 동적으로 조절하는 것이다. 페이지 폴트 빈도가 높으면 워킹 세트 크기를 늘리고, 낮으면 줄이는 방식으로 메모리 사용을 최적화한다.

 

스레싱 증상

CPU가 스레싱을 방지하기 위해서 페이지 교체를 최적화하지만 여전히 스레싱은 발생할 수 있다. 따라서 스레싱 징후를 파악하고 대처할 수 있어야한다. 스레싱 징후를 파악하기 위해서는 CPU 이용률, 로드 에버리지(Load Average), 메모리 이용률, 스왑인/아웃 페이지 수 등의 지표를 종합적으로 모니터링해야 한다. 로드 에버리지의 개념에 대해서는 4-1.에서 확인할 수 있다.

 

1. 로드 에버리지가 높은데 CPU 이용률도 높다면: CPU 리소스 부족. 프로세스 수를 줄이거나, 어플리케이션 코드를 최적화해 CPU 사용량을 줄이거나, CPU를 업그레이드하거나, 분산 처리 시스템을 도입(같은 구성의 서버를 추가하여 로드밸런서로 요청을 분산)하는 것을 고려해볼 수 있다.

 

2. 로드 에버리지가 높은데 CPU 이용률이 낮다면: 많은 프로세스가 생성되어 있지만 실제로 CPU를 사용하지 못하고 있음을 나타낸다. 이는 디스크 I/O 병목, 네트워크 I/O 병목, 메모리 부족으로 인한 스왑등이 문제일 수 있다. 병목 지점을 파악하고 해결해야한다.

 

3.메모리 이용률이 높고, 스왑인/아웃이 높다면: 로드 에버리지가 높은데 CPU 이용률이 낮다면 메모리 이용률과 스왑인/아웃을 체크해봐야한다. 메모리 이용률이 높고, 스왑인/아웃이 많다면 메모리 부족으로 인한 스레싱 상태일 수 있다.

 

정리하자면, CPU 사용률이 낮고, load average가 높으며, 메모리 이용률이 높고, 스왑인 및 스왑아웃이 많이 발생하면 시스템이 스레싱 상태에 있을 가능성이 큽다. 이러한 지표들을 모니터링하여 스레싱 여부를 확인하고, 필요시 적절한 조치를 취하는 것이 중요하다.

 

해결책

스레싱을 해결하기 위해서 다음과 같은 조치를 취할 수 있다.

 

1. 메모리 증설: 물리적 메모리 RAM을 추가하여 시스템 전체 메모리 용량을 증가 시킨다.

2. 프로세스 수 줄이기: 동시에 실행되는 프로세스 수를 줄여 각 프로세스가 충분한 메모리를 확보할 수 있도록 한다.

3. 어플리케이션 최적화: 특정 프로세스가 메모리를 극단적으로 소비하고 있지 않은지 체크하고 어플리케이션의 메모리 사용을 최적화한다.

 

스왑 공간을 확장하는 것은 스레싱 문제를 해결하지 못한다. 스왑 공간을 확장하면 일시적으로 메모리 부족 문제를 완화할 수 있지만, 스레싱의 근본적인 원인인 물리 메모리 부족을 해결하지 못해 스레싱이 여전히 발생할 수 있다.

4. 모니터링 실습

현재 사용중인 컴퓨터의 운영체제는 macOS 이다. linux에서 시스템 모니터링을 위해 사용하는 명령어와는 다른 명령어를 사용해서 시스템 모니터링이 가능하다. macOS의 Acitivity Monitor(활성 상태 보기)를 사용해 그래픽으로도 확인이 가능하지만 여기에서는 터미널 명령어를 사용해 확인해본다.

4-1. Load Average  및 CPU 사용률

로드 에버리지란 시스템의 평균 부하를 나타내며 1분, 5분, 15분 동안 시스템에서 실행중이거나 대기 중인 프로세스의 수를 나타낸다. 이때 다음과 같은 프로세스가 포함된다.

- Running 상태: 현재 CPU에 의해서 실행되고 있는 프로세스

- Runnable(Ready) 상태 : 실행 준비가 되어 있고, CPU에서 실행되기를 기다리고 있는 프로세스

- Uninterruptible Sleep 상태: 해당 작업이 완료될 때까지 인터럽트에 의해 중단될 수 없는 상태. 디스크 I/O나 네트워크 I/O와 같은 블로킹 작업 중에 발생한다. 키보드 I/O나 타이머 만료 등을 기다리는 것과 같이 대기 중이지만 시그널에 의해 깨울 수 있는 상태는 포함되지 않는다.

 

로드 에버리지는 CPU 코어수에 따라 다른 CPU 부하 상황을 의미한다. CPU 코어 수가 8개인데 로드 에버리지가 4인 경우 CPU가 여유가 있는 상황이다. CPU는 한번에 8개의 프로세스가 실행될 수 있는데 프로세스가 다른 이유(ex 디스크 I/O)로 대기중일 수 있기 때문이다. 이 경우 로드 에버리지는 낮다고 판단할 수 있다. 반면, CPU 코어 수가 2개인데 로드 에버리지가 4인경우 CPU를 100% 사용해도 모든 프로세스를 실행할 수 없어 프로세스가 대기 중인 상황일 수 있다. 이 경우 로드 에버리지는 높고 CPU에 부하가 걸린 상황이라고 판단할 수 있다.

 

로드 에버리지가 높다는 것은 응답이 지연되는 CPU부하가 높은 상황이라고 할 수 있다. 또한, 로드 에버리지 값이 15분 값에 비해 5분 1분 값이 높아진다면 점점 부하가 심해지고 있다고 판단할 수 있다.

 

아래는 `sysctl`명령어로 cpu 코어의 수를 확인하고 `top` 명령어로 load average를 확인한 것이다. cpu 코어의 수가 8개인데 로드 에버리지가 약 3으로 CPU 부하가 낮은 상황이다. 실제로 CPU 이용률이 약 40% 정도인 것을 확인할 수 있다.

CPU 코어의 수를 확인한다.

top 명령어로 실시간 시스템 사용 상황(로드에버리지, 메모리, 네트워크, 디스크)을 확인할 수 있다. -l 1 옵션으로 스냅샷을 한번만 출력한다. top 결과에는 프로세스별 사용 상황도 포함되는데 head -n 10으로 위에서 10줄만 출력하여 프로세스 별 사용 상황을 제외한다.

로드에버리지는 다음과 같이 `uptime` 명령어로 확인할 수도 있다.

4-2. 메모리

MacOS에서는 직접적으로 확인하기 어려운 지표들이 많아 쉘 스크립트를 사용하였다. 사용한 쉘 스크립트와 실행 결과는 다음과 같다. 시스템 리소스 사용 상황을 체크하기 위해 사용한 명령어는 `sysctl` , `top`, `vm_stat`이다.

mem_size=$(sysctl hw.memsize | awk '{print $2 / 1048576}')
swap_size=$(sysctl vm.swapusage | awk '{print $4}' | awk -F'.' '{print $1}')
total_vm=$((mem_size + swap_size))

echo "Physical Memory: $mem_size MB" // 물리 메모리 크기
echo "Swap Memory: $swap_size MB" // 스왑 영역 크기
echo "Total Virtual Memory: $total_vm MB" // 총 메모리 크기
echo "======================="

phys_mem_usage=$(top -l 1 | grep PhysMem | awk '{print $2}' | awk -F'.' '{print $1}')
swap_mem_usage=$(sysctl vm.swapusage | awk '{print $7}' | awk -F'.' '{print $1}')

echo "Physical Mem Usage: $phys_mem_usage / $mem_size MB" // 물리 메모리 사용량/ 총 물리 메모리
echo "Swap Mem Usage: $swap_mem_usage / $mem_size MB"// 스왑 영역 사용량/총 스왑 영역

page_size=$(vm_stat | grep "page size of" | awk '{print $8}')
total_memory=$(sysctl -n hw.memsize)
total_pages=$((total_memory / page_size))

echo "Page Size: $page_size Bytes" // 페이지 사이즈
echo "Total pages: $total_pages 개" // 페이지 개수(총 메모리/페이지 사이즈)
echo "======================="

swap_ins=$(vm_stat | grep -i "Swapins" | awk '{print $2}')
swap_outs=$(vm_stat | grep -i "Swapouts" | awk '{print $2}')

echo "SwapIns: $swap_ins" // 스왑 영역에서 메모리로 로드된 페이지 수
echo "SwapOuts: $swap_outs" // 메모리에서 스왑 영역으로 방출된 페이지 수
echo "======================="

 

운영체제는 가상 메모리를 사용해서 프로세스의 메모리를 추상화한다. 프로세스의 메모리는 페이지로 분할되어서 물리 메모리에 할당된다. 또한, 운영체제는 스왑 영역을 사용해서 실제 물리 메모리보다 더 큰 공간을 메모리처럼 사용할 수 있도록 한다. 컴퓨터가 사용가능한 메모리는 물리 메모리와 스왑 영역의 합계이다.

 

현재 컴퓨터에서 사용가능한 물리메모리(Physical Memory)는 약 8G이고, 스왑 영역(Swap Memory)은 약 8G이다. 따라서, 사용가능한 총 메모리의 양(Total Virtual Memory)은 약 16G라고 할 수 있다. 이 중에서 물리 메모리의 약 7.5G는 사용중(Physical Mem Usage)이고, 스왑 영역도 7.5G정도 사용(Swap Mem Usage)하고 있다. 물리 메모리와 스왑 메모리 모두 약 90정도 사용중으로 메모리 압력이 높은 상태이다. 현재 운영체제는 각 페이지(Page Size)를 16KB사이즈로 관리하고 있다. 또한 약 6억번 정도의 스왑이 일어났다. 추측하기에는 컴퓨터를 구동해둔채 끄지 않고 사용해와서 그 동안에 스왑인과 스왑아웃 횟수가 누적되었던 것 같다. 현재 메모리 압력이 높고 스왑이 많이 발생한 것으로 봐 스레싱이 발생할 가능성이 높다. 메모리 여유를 확보하는 것이 필요할 수 있다.

 

위에서 확인한 수치를 Activity Monitor에서 확인해보면 비슷한 수치임을 확인할 수 있다. 각 수치의 의미는 다음과 같다.

 

- 물리적 메모리: 컴퓨터에 설치된 ram의 크기. 물리적 메모리 크기

- 사용된 메모리: 물리적 메모리 중 실제 운영체제나 프로세스에 의해서 사용중인 메모리 크기

- 캐시된 파일: 시스템에서 성능을 향상하기 위해 사용하지 않은 메모리로 캐시한 파일의 크기

- 사용된 스왑 공간: 더 최근에 사용된 데이터를 위한 메모리 공간을 만들기 위해 일시적으로 디스크로 이동된 데이터 크기

 

[참고] 가상 주소 공간

위에서 살펴보면 가상 주소 공간의 사이즈는 180 테라바이트로 물리 메모리와 스왑 영역을 할당한 것보다 매우 크다.
vsize는 프로세스가 사용하는 전체 가상 주소 공간을 나타내며, 실제로 물리 메모리나 스왑에 할당되지 않은 공간도 
포함하기 때문이다. 프로세스는 실제로 사용하지 않는 큰 주소공간을 예약할 수 있기 때문에 vsize는 
가상 메모리 크기보다 훨씬 크다. 

프로세스는 큰 주소 공간을 예약해서 스레드와 힙 영역사이의 큰 공간을 두어 스레드와 힙 영역이 서로 충돌하지 
않도록 한다.

정리

✅ 가상 메모리란 불연속적인 물리적 메모리를 연속적인 논리 메모리로 추상화해서 프로세스가 사용할 수 있게 하는 메모리 기법이다. 프로세스는 연속적인 가상의 공간을 사용하고 운영체제는 이를 페이지로 나누어서 물리 메모리에 할당한다.

 

 운영체제는 가상 메모리 기법에 더해 요구 페이징과 스와핑을 사용해서 더욱 메모리를 효율적으로 사용한다. 요구 페이징을 통해 실제로 프로세스가 사용하는 페이지만 디스크로 로드하고, 디스크의 스왑 영역을 메모리처럼 사용해 물리 메모리보다 더 큰 공간을 메모리처럼 사용한다.

 

 프로세스에는 할당되었지만 물리 메모리에는 할당되지 않은’ 상태 요구 페이징에 의해서 처음 참조되기 전이라 물리 메모리에 매핑되기 전이거나, 물리 메모리가 부족으로 스왑 아웃된 페이지인 상태이다. 이러한 페이지에 접근시 MMU는 페이지 폴트 인터럽트를 발생시킨다.

  • 페이지가 아직 로드되지 않아 페이지 폴트가 발생한 경우, 디스크에서 해당 페이지를 읽어와 물리 메모리에 로드한다.
  • 페이지가 아직 할당되지 않아 페이지 폴트가 발생한 경우, 런타임 바인딩을 실행한다.
  • 페이지가 스왑 아웃되어 페이지 폴트가 발생한 경우, 스왑 공간에서 해당 페이지를 다시 물리 메모리로 로드합니다

 스왑은 디스크 I/O가 필요해 CPU 이용률을 떨어뜨리므로 운영체제는 페이지 교체 알고리즘을 통해 최적화한다. 물리 메모리의 크기에 비해 너무 많은 프로세스가 동시에 실행되면 스왑이 많이 발생해 CPU 이용률이 떨어지는 스레싱이 발생할 수 있다. CPU 이용률, 로드 에버리지, 메모리 사용량, 스왑 상황을 종합적으로 모니터링에 스레싱 징후를 파악하고 대처할 수 있어야 한다.

 

 MacOS에서 시스템 상황을 진단하기위해 `top`, `iostat`, `vm_stat`, `sysctl`등의 명령어를 사용할 수 있다.

 

'study > cs' 카테고리의 다른 글

CPU 스케줄링 알고리즘 및 관련된 여러 주제  (0) 2024.04.24