가상 메모리(virtual memory)는 메모리 관리 기법의 하나이다.
컴퓨터가 실제로 이용 가능한 메모리 자원을 추상화하여 이를 사용하는 사용자들에게 매우 큰 메모리로 보이게 만드는 것을 말한다.
가상 주소(logical address): 가상적으로 주어진 주소
실제 주소(physical address): 실제 메모리상에 있는 주소
가상 주소는 메모리관리장치(MMU)에 의해 실제 주소로 변환됨 -> 사용자는 실제 주소를 의식할 필요 없이 프로그램 구축 가능
가상 메모리는 '페이지 테이블'로 관리된다.
페이지 테이블: 가상 주소와 실제 주소가 매핑 되어있고 프로세스의 주소 정보가 들어 있다. 이때 속도 향상을 위해 TLB를 쓴다.
TLB: 메모리와 CPU 사이에 있는 주소 변환을 위한 캐시.
페이지 테이블에 있는 리스트를 보관하며 CPU가 페이지 테이블까지 가지 않도록 해 속도를 향상시킬 수 있는 캐시 계층
스와핑
: 가상 메모리에는 존재하지만 실제 메모리인 RAM에는 현재 없는 데이터나 코드에 접근할 경우 페이지 폴트가 발생한다. 이때 메모리에서 당장 사용하지 않는 영역을 하드디스크로 옮기고 하드디스크의 일부분을 마치 메모리처럼 불러와 사용하는 것을 말한다.
-> 마치 페이지 폴트가 일어나지 않은것처럼 만든다.
페이지 폴트
: 프로세스의 주소 공간에는 존재하지만 지금 이 컴퓨터의 RAM에는 없는 데이터에 접근했을 경우에 발생한다.페이지폴트와 스와핑이 발생하는 과정
1. 명령어가 유효한 가상 주소에 접근 but 해당 페이지가 없다? 트랩 발생 -> 운영체제에 알림 2. 운영체제는 실제 디스크로부터 사용하지 않은 프레임을 찾음 3. 해당 프레임을 실제 메모리로 가져오고 페이지 교체 알고리즘 기반으로 특정 페이지와 교체 -> *이때 스와핑이 일어난디* 4. 페이지 테이블을 갱신하고 해당 명령어를 재시작페이지(page): 가상 메모리를 사용하는 최소 크기 단위
프레임(frame): 실제 메모리를 사용하는 최소 크기 단위
스레싱(thrashing)은 메모리의 페이지 폴트율이 높은 것을 의미한다.
컴퓨터의 심각한 성능 저하를 초래한다.
메모리에 너무 많은 프로세스가 동시에 올라감 -> 스와핑 자주 발생
=> 스레싱 발생!!
페이지 폴트가 일어나면 CPU 이용률이 낮아짐 -> 운영체제는 "CPU가 한가한가?^^"라고 생각해서 가용성을 높이기 위해 더 많은 프로세스를 메모리에 올리게 됨
=> 악순환 반복되며 스레싱이 일어나는것
해결 방법
1. 메모리 늘리기
2. HDD 사용한다면 SSD로 바꾸기
운영체제에서 해결하는 방법
1. 작업 세트
2. PFF
작업 세트
: 작업 세트(working set)는 프로세스의 과거 사용 이력인 지역성을 통해 설정된 페이지 집합을 만들어서 미리 메모리에 로드하는 것이다.
-> 탐색에 드는 비용, 스와핑을 줄일 수 있다.
PFF
: PFF(Page Fault Frequency)는 페이지 폴트 빈도를 조절하는 방법으로 상한선과 하한선을 만드는 방법이다.
상한선 도달 -> 프레임 늘림
하한선 도달 -> 프레임 줄임
메모리를 프로그램에 할당할 때는 시작 메모리 위치, 메모리의 할당 크기를 기반으로 할당한다.
연속 할당, 불연속 할당으로 나뉨
연속 할당
: 메모리에 '연속적으로' 공간을 할당하는 것
이는 또 두가지로 나뉨
- 고정 분할 방식
: 고정 분할 방식(fixed partition allocation), 메모리를 미리 나누어 관리하는 방식
메모리가 미리 나누어져 있기 때문에 융통성이 없음.
내부 단편화 발생- 가변 분할 방식
: 가변 분할 방식(variable partition allocation), 매 시점 프로그램의 크기에 맞게 동적으로 메모리를 나눠 사용하는 방식
외부 단편화 발생
최초적합(first fit), 최적적합(best fit), 최악적합(worst fit)이 있음
| 이름 | 설명 |
|---|---|
| 최초적합 | 위쪽(아래쪽)에서 시작해서 홀을 찾으면 바로 할당 |
| 최적적합 | 들어갈수 있는 가장 작은 홀부터 할당 |
| 최악적합 | 제일 큰 홀에 할당 |
내부 단편화(internal fragmentation)
: 메모리를 나눈 크기보다 프로그램이 작아서 들어가지 못하는 공간이 많이 발생하는 현상(프로그램이 들어가고도 메모리에 공간이 많이 남게됨)
외부 단편화(external fragmentation)
: 메모리를 나눈 크기보다 프로그램이 커서 들어가지 못하는 공간이 많이 발생하는 현상
홀
: 할당할 수 있는 비어있는 메모리 공간
메모리를 연속적으로 할당하지 않는다. 페이징 기법 -> 현대의 운영체제가 쓰는 방법
메모리를 동일한 크기의 페이지(보통 4KB)로 나누고 프로그램마다 페이지 테이블을 두어 이를 통해 메모리에 프로그램을 할당한다.
페이징 기법 외에 세그멘테이션, 페이지드 세그멘테이션이 있다.
페이징
: 동일한 크기의 페이지 단위로 나누어 메모리의 서로 다른 위치에 프로세스를 할당
- 장점: 홀의 크기가 균일
- 단점: 주소 변환이 복잡
세그멘테이션
: 페이지 단위가 아닌 의미 단위인 세그먼트로 나누는 방식.
프로세스를 이루는 메모리는 코드 영역, 데이터 영역, 스택 영역, 힙 영역으로 이루어지는데 코드와 데이터로 나누거나 코드 내의 작은 함수를 세그먼트로 놓고 나눌 수도 있다.
- 장점: 메모리를 의미 단위로 나누기 때문에 프로그램의 코드 영역(함수, 루틴 등)을 쉽게 공유할 수 있다.
(예 - 여러 프로세스가 동일한 라이브러리나 함수(수학 계산 라이브러리 등)를 사용할 때, 해당 코드 세그먼트를 공유 메모리에 두고 재사용 가능)
-> 메모리 낭비를 눌이고 효율성을 높일 수 있다.- 장점: 세그먼트마다 접근 권한을 설정할 수 있어 보안성이 강화된다.
(예 - 코드 세그먼트는 실행만 가능하고 쓰기 권한이 없도록 설정)
이러한 접근 제어를 통해 프로세스가 의도하지 않은 영역을 수정하거나 실행하지 못하게 막아 버퍼 오버플로우 공격을 방지할 수 있다.- 단점: 홀크기가 균일하지 않음
페이지드 세그멘테이션
: 페이징 + 세그멘테이션
프로그램을 세그멘테이션으로 나눠 보안과 공유측면에서 강점을 두고 동일한 크기의 페이지 단위로 나눈다.
: 먼미래에 참조되는 페이지와 현재 할당하는 페이지를 바꾸는 알고리즘(가장 좋은 방법)
그러나 우리는 미래에 사용되는 프로세스를 알 수 없다.
-> 사용할 수 없는 알고리즘임. 그러나 가장 좋은 알고리즘이기 때문에 다른 알고리즘과의 성능 비교에 대한 상한기준(upper_bound)를 제공한다.
=> 내가 사용하는 알고리즘이 오프라인 알고리즘과 얼마나 가까운 성능을 내고있는가를 판단하는 기준점이 된다.
FIFO
: FIFO(First In First Out), 가장 먼저 온 페이지를 교체 영역에 가장 먼저 놓는 방법을 의미한다.
LRU
: LRU(Least Recently Used), 참조가 가장 오래된 페이지를 바꾼다.
오래된 것을 파악하기 위해 각 페이지마다 계수기, 스택을 두어야하는 문제가 있음
NUR
: NUR(Not Used Recently), clock 알고리즘이라고도 한다. 0과 1을 가진 비트를 둔다.
1: 최근에 참조됨
0: 참조되지 않음
시계 방향으로 돌리면서 0을 찾고 0을 찾은 순간 해당 프로세스를 교체하고 해당 부분을 1로 교체한다.
LFU
: LFU(Least Frequently Used), 가장 참조 횟수가 적은 페이지를 교체