프로그래머는 컴퓨터 내의 한정된 메모리를 효율적으로 활용해야 한다.
그를 위한 메모리 관리 방법들에 대해 알아본다.
가상메모리란 컴퓨터가 실제로 이용 가능한 메모리 자원을 추상화하여 이를 사용하는 사용자에게 매우 큰 메모리로 보이게 만드는 것을 말한다.
가상 메모리는 프로세스가 실행될 때, 실행에 필요한 일부만 메모리에 올리고 나머지는 하드에 남겨서 하드가 RAM의 보조 기억장치처럼 작동하도록 한다. 이를 통해 RAM과 하드를 병합한, 하나의 크고 빠른 기억 장치로 활용하는 원리이다.
이때, 가상적으로 주어진 주소를 가상 주소(logical address)라고 하며, 실제 메모리(RAM)상에 있는 주소를 실제 주소(physical address)라고 한다.
가상 주소는 메모리 관리장치(MMU)
에 의해 실제 메모리 주소로 변환되며, MMU 덕분에 사용자는
실제 주소를 의식하지 않아도 프로그램을 구축할 수 있게 된다.
가상 메모리는 가상 주소와 실제 주소가 1:1 매핑되어 있고 프로세스의 주소 정보가 들어있는
페이지 테이블
로 관리된다. 이때 속도 향상을 위해 TLB
를 사용한다.
TLB(Translation Lookaside Buffer)
메모리와 CPU 사이에 있는 '주소 변환'을 위한 캐시 메모리
최근에 일어난 주소 변환을 저장해두고, 접근 요청이 들어왔을 때 CPU가 페이지 테이블까지 가지 않으므로 속도를 향상시킬 수 있다.
프로세스의 모든 데이터를 메모리에 올리지 않고 CPU가 요청할 때, 프로세스의 데이터를 RAM에 올리는 것을 말한다.
가상 메모리의 프로세스 주소 공간에는 존재하지만 실제 메모리인 RAM에는 존재하지 않는 데이터나 코드에 접근하는 경우 페이지 폴트
가 발생한다.
이때, 메모리에서 사용하지 않고 있는 영역을 HDD로 옮기고 HDD의 일부를 메모리처럼 사용하는 것을 스와핑이라고 한다.
스와핑을 통해 페이지 폴트가 일어나지 않은 것처럼 만들 수 있다.
- CPU가 RAM를 확인하고 해당 페이지가 없으면 트랩을 발생시켜 OS에 알린다.
- OS가 CPU의 동작을 잠시 멈춘다.
- OS는 페이지 테이블에서 가상 메모리에
페이지
가 존재하는지 확인한다. - 페이지가 없다면 프로세스를 중단하고 현재 메모리에 비어있는
프레임
이 있는지 확인한다. - 메모리에도 없다면 스와핑이 발동한다.
- 비어있는 프레임에 해당 페이지를 로드하고 페이지 테이블을 최신화한다.
- CPU를 다시 동작시킨다.
페이지
: 가상 메모리를 사용하는 최소 크기 단위
프레임
: 실제 메모리를 사용하는 최소 크기 단위
스레싱은 메모리의 페이지 폴트율이 높은 것을 의미한다.
스레싱이 발생해서 성능 저하가 일어나는 과정은 다음과 같다.
메모리에 너무 많은 프로세스가 동시에 올라감 👉🏻 스와핑 ⬆️ (페이지 폴트 ⬆️) 👉🏻 CPU 이용률 ⬇️ 👉🏻 OS가 CPU에 더 많은 프로세스를 올림
이를 해결하기 위한 방법으로는 메모리를 늘리거나 HDD를 SSD로 변경하는 등의 방법이 있지만,
OS에서 해결할 수 있는 방법인 작업 세트와 PFF를 알아보자.
- 프로세스의 과거 사용 이력인 지역성(locality)을 통해 결정된 페이지 집합을 만들어서 메모리에 미리 업로드하는 것
- 메모리에 미리 업로드하므로, 탐색에 대한 비용을 줄일 수 있으며 스와핑도 줄일 수 있다.
- 상한선과 하한선을 만들어 페이지 폴트 빈도를 조절하는 방법
- 상한선에 도달한다면 프레임을 늘리고 하한선에 도달한다면 프레임을 줄인다.
메모리에 프로그램을 할당할 때는 시작 메모리의 위치
와 메모리의 할당 크기
를 기반으로 할당한다.
이러한 할당 방법에는 연속 할당과 불연속 할당이 있다.
연속 할당은 메모리에 순차적으로 공간을 할당하는 것을 의미한다.
연속 할당의 방식에는 두 가지가 있다.
- 고정 분할 방식 (fixed partition allocation)
- 메모리를 미리 나누어 관리하는 방식
- 프로세스가 필요로 하는 양보다 더 큰 메모리가 할당되어 메모리 공간을 낭비하는 현상인
내부 단편화
가 발생한다.
- 가변 분할 방식 (variable partition allocation)
- 매 시점 프로그램의 크기에 맞게 메모리를 동적으로 나눠 사용하는 방식
- 메모리 공간 사이에 빈 공간이 많아서 다른 프로세스가 들어오지 못하는 현상인
외부 단편화
가 발생할 수 있다.
[가변 분할 방식의 종류] - 최초 적합: 위쪽이나 아래쪽부터 시작해서 홀을 찾으면 바로 할당 - 최적 적합: 프로세스의 크기 이상인 공간 중 가장 작은 홀부터 할당 - 최악 적합: 프로세스의 크기와 가장 많이 차이나는 홀에 할당
불연속 할당은 메모리를 연속적으로 할당하지 않는 할당 방법이다.
이는 연속 할당의 문제점인 내부/외부 단편화
를 해결하기 위한 방법이기도 하다.
불연속 할당의 방식에는 세 가지가 있다.
- 페이징 (paging) 기법
- 가상 메모리를 동일한 크기의 페이지(4KB) 단위로 나누어, 프로그램마다 페이지 테이블을 두고 이를 통해 페이지와 프레임을 1:1 매핑시켜 실제 메모리에 접근하는 방식이다.
- 장점:
홀
의 크기가 균일해진다. (외부 단편화 해결) - 단점: 주소 변환이 복잡해진다.
- 세그멘테이션 (segmentation) 기법
- 프로그램을 페이지 단위(일정한 크기)가 아닌 논리적 단위인
세그먼트 단위
(가변적인 크기)로 나누는 방식이다. - 장점: 공유, 보안이 가능하다. 내부 단편화 문제를 해결할 수 있다.
- 단점: 홀 크기가 균일하지 않다.(외부 단편화 문제가 발생할 수 있음)
- 프로그램을 페이지 단위(일정한 크기)가 아닌 논리적 단위인
- 페이지드 세그멘테이션 (paged segmentation)
- 프로그램를 세그먼트 단위로 나눠 보안 측면에 강점을 두고, 동일한 크기의 페이지 단위로 나누는 방식
홀(hole)
: 비어있는 메모리 중 할당할 수 있는 공간을 의미한다.
멀티 프로그래밍 환경에서 프로세스의 원활한 수행을 위해 RAM에서 디스크 스왑영역으로 교체할 페이지를 선택하는 알고리즘을 의미한다. 페이지 교체 알고리즘은 크게 네 가지가 있다.
나중에 참조되는 페이지와 현재 할당하는 페이지를 바꾸는 '가장 좋은' 알고리즘 (실제로 구현하기 거의 불가능함)
미래에 사용되는 프로세스를 알 수 없기 때문에 알고리즘과의 성능 비교에 대한 상한기준을 제공한다.
가장 먼저 온 페이지를 교체 영역에 가장 먼저 놓는 방법을 의미한다.
참조가 가장 오래된 페이지와 바꾸는 방식을 의미한다.
다만, '오래된' 것을 파악하기 위해 각 페이지마다 계수기와 스택을 두어야 하는 문제점이 있다.
LRU를 구현할 때는 보통 해시 테이블
이나 이중 연결 리스트
자료구조로 구현한다.
[NUR (Not Used Recently) 알고리즘]
- LRU에서 발전한 알고리즘으로, 일명 clock 알고리즘이라고도 한다.
- 0과 1을 가진 비트를 두며 1은 '최근에 참조됨', 0은 '참조되지 않음'을 의미한다.
- 시계 방향으로 돌면서 0을 찾은 순간 해당 프로세스를 교체하고 1로 바꾸는 알고리즘이다.
가장 참조 횟수가 적은(가장 적게 사용된) 페이지와 바꾸는 방식을 의미한다.