(참고: Fastcampus 강의, 강민철의 인공지능 시대 필수 컴퓨터 공학 지식)
5. 가상 메모리 관리
Contents
- 페이징 & 페이지 테이블
- 가상 메모리
- 요구 페이징 & 스래싱
- 페이지 교체 알고리즘
- copy-on-write
- 캐시 메모리 정책
(1) 페이징 & 페이지 테이블
a) 스와핑
프로세스를 보조기억장치의 일부 영역으로 쫓아내고,
당장 필요한 프로세스를 적재하는 메모리 관리 기법
용어
- 스왑 아웃 (swap-out): 프로세스를 보조기억장치의 일부 영역으로 쫓아내는 것
- 스왑 인 (swap-in): 스왑 아웃된 프로세스를 메모리에 적재하는 것
- 스왑 영역: 스왑 아웃된 프로세스가 적재되는 보조기억장치 영역
b) 연속 메모리 할당
프로세스를 메모리에 연속적으로 배치하는 방식
부작용: 외부 단편화
- 빈 공간이 생기는 메모리 낭비 현상!
c) 페이징
외부 단편화를 해결하는 방법은?
\(\rightarrow\) 페이징 (paging) = 메모리와 프로세스를 일정 단위로 자르자!
상세 방식
- 물리 메모리를 “프레임 (frame)”이라는 일정한 크기로 나누고
- 프로세스를 “페이지 (page)”라는 일정한 크기로 나눈 뒤
- “페이지를 프레임에 매핑”하는 메모리 관리 방식
d) 페이지 인 & 페이지 아웃
(2) 가상 메모리
프로세스의 일부만을 적재하여, “실제 물리 메모리보다 큰 프로세스”를 실행하는 기술!
페이징은 가장 대표적인 가상메모리 관리 기법
a) 가상메모리 - 비유 (feat. ChatGPT)
너가 1,000페이지짜리 책을 읽는다고 해보자. 근데 손에 들 수 있는 건 100페이지짜리 얇은 노트뿐이야.
그래서 필요한 부분만 "복사해서 노트에 옮기고" 읽는 거지.
나중에 다른 부분이 필요하면, "다시 교체해서" 읽고.
이렇게 하면 책 전체가 손에 없어도, 읽는 데는 문제 없어!
- 디스크(책 전체)에 프로그램 전체가 있고
- RAM(작은 노트)에는 실행에 필요한 일부분만 임시로 적재
이게 어떻게 가능??
운영체제가 페이지 단위로 메모리를 나눠서 관리하고,
- 실행 중에 필요한 페이지만 메모리에 로딩해 줘
- 필요한데 메모리에 없으면 → 페이지 폴트(page fault) 발생 → 디스크에서 RAM으로 불러옴
이걸 페이지 교체(page replacement) 전략으로 자동 처리해 줘
b) 페이지 테이블
어떤 페이지 & 어떤 프레임 매핑 되었는지 어떻게 암?
\(\rightarrow\) via “페이지 테이블”
- 프레임과 페이지의 매핑 정보를 담고 있는 테이블
“프로세스”마다 페이지 테이블을 가지고 있음.
c) 페이지 테이블 베이스 레지스터 (PTBR)
페이지 테이블이 어디에 있는지를 CPU가 알 수 있게 해주는 레지스터
페이지 테이블은 어디에 있는 것이 좋을까?
-
후보 1) 메모리
\(\rightarrow\) No! 메모리 접근 시간 2배 필요
- (1) 페이지 테이블 접근
- (2) 프레임 접근
-
후보 2) 캐시 메모리
\(\rightarrow\) Yes! TLB (Translation Look-aside Buffer)
- 페이지 테이블의 캐시 메모리
[페이지 테이블 내 정보]
유효 비트 (valid bit)
- 접근하려는 페이지가 “보조기억 장치”에 있는지, “메모리”에 있는지
- 1 = 메모리에 적재되어 있음 O
- 0 = 메모리에 적재되어 있지 X
- If 적재 X (즉, 보조기억장치에 있는 경우)? => 페이지 폴트 (page fault)
- Step 1) 작업 내역 백업
- Step 2) Page fault 루틴 실행 - 접근하려는 페이지 적재
- Step 3) 유효비트 0 \(\rightarrow\) 1로 변경
- Step 4) 접근하려는 페이지 접근
보호 비트 (protection bit)
- r/w/x를 통해 읽기/쓰기/실행 등의 구분
참조 비트 (reference bit)
- 접근한 적이 있는 페이지인지 구분
수정 비트 (modify bit / dirty bit)
- 쓰기 작업을 한 적이 있는 페이지인지
d) 계층적 페이징
페이지 테이블 크기를 줄이기 위해!
(3) 요구 페이징 & 스래싱
a) 요구 페이징
처음부터 모든 페이지를 적재하지 않고, 페이지 폴트가 발생하면 그 때 페이지를 적재한다!
순수 요구 페이징
- (처음) 아무 페이지도 적재 X
- 첫 명령어 실행부터 페이지 폴트가 발생할 것!
- 적당한 페이지가 적재된 이후부터는, 페이지 폴트가 감소할 것!
페이지 폴트가 적게 발생하는 방법은?
- (근본적으로는) 물리 메모리가 크면 됨
- 프레임이 \(\infty\) \(\rightarrow\) 무한히 많은 페이지 적재 가능
- 프레임이 1개 \(\rightarrow\) 페이지 접근할 때마다 페이지 폴트가 발생
b) 스래싱
프로세스 실행 시간보다, “페이징에 더 많은 시간이 소요”되는 문제
( Due to 지나친 페이지 폴트! )
- 동시에 실행되는 프로세스 수를 늘린다고해서, 반드시 CPU 이용률이 비례해서 높아지는 것이 아닌 이유임
c) 페이지 교체 알고리즘
현실적으로, 물리적인 메모리 (프레임)은 무한하지 않음.
그렇다면, 어떻게 페이지 폴트 횟수를 줄일 수 있을까?
\(\rightarrow\) 페이지 교체 알고리즘! (뒤에서 다룰 것)
- 보조기억장치로 내보낼 페이지 & 메모리에 적재할 페이지를 잘 선별하면 된다.
(4) 페이지 교체 알고리즘
메모리에 적재된 페이지 중, 페이지-아웃시킬 페이지를 선정하는 방법
- 좋은 페이지 교체 알고리즘 = 페이지 폴트를 “적게 일으키는” 알고리즘
페이지 참조열
- CPU가 참조하는 페이지 중 연속된 페이지를 생략한 페이지열
- Example)
참조한 페이지: 2 2 2 3 5 5 5 3 3 7
페이지 참조열: 2 3 5 3 7
페이지 교체 알고리즘의 예시
- (1) FIFO
- (2) 2차 기회 FIFO
- (3) 최적
- (4) LRU
(5) copy-on-write
핵심: 복사는 나중에! 처음에는 공유하고, 진짜 수정하려고 할 때만 복사하는 기술!
비유 (feat. ChatGPT)
너랑 친구가 같은 문서를 읽고 있다고 해보자.
둘 다 "복사하지 않고" 같은 문서를 보고 있어 (공유 중).
그런데 친구가 갑자기 내용을 수정하려고 하면?
=> 그 순간 "자기만의 복사본을 만들어서 수정"해.
fork
-
대표적으로 fork() 시스템 호출에서 사용
- fork(): 부모 프로세스를 복제해서 자식 프로세스를 만듬
-
실제로는 메모리를 복사하지 않고, 부모와 자식이 같은 페이지를 공유
\(\rightarrow\) 둘 중 누가 수정하려고 하면 그때 진짜 복사
장점
- 메모리 절약: 처음엔 복사하지 않으니 자원 아낌
- 속도 향상: 필요할 때만 복사하니까 효율적
Summary
항목 | 설명 |
---|---|
언제 복사? | 수정하려고 할 때 (write 시점) |
초기 상태 | 페이지를 공유 (읽기 전용) |
목적 | 성능 최적화, 메모리 절약 |
사용 예 | fork(), 가상 메모리 시스템, 일부 STL 컨테이너(C++) |
(6) 캐시 메모리 정책
a) 캐시 메모리 복습
-
CPU와 메모리 간의 속도 차를 극복하기 위해
-
CPU와 메모리 사이에 위치
( CPU에서 사용할 법한 정보를 미리 가져와 저장 )
-
레지스터보다 용량이 크고, 메모리보다 빠른 SRAM 기반
-
종류: L1,L2,L3 cache
-
캐시 히트 & 캐시 미스
b) 참조 지역성의 원리
캐시 메모리에는, “CPU가 자주 사용할 법한 내용”을 담는다.
Q) 자주 사용할 법한 내용은?
A) 참조 지역성의 원리
- 시간 지역성: CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향
- 공간 지역성: CPU는 접근한 메모리 공간 근처를 접근하려는 경향
c) 캐시 메모리 쓰기 정책: 데이터 일관성
캐시 메모리에 데이터를 새롭게 쓰는 상황!
\(\rightarrow\) 캐시 메모리 & 메모리 간의 “데이터 일관성”은 필수적이다!
데이터 일관성이 깨지는 case:
d) 즉시 쓰기 & 지연 쓰기
즉시 쓰기 (write-through)
-
캐시 메모리 & 메모리에 “동시에” 쓰기
-
메모리를 항상 최신 상태로 유지!
-
다만, 데이터를 쓸 때마다 메모리를 참조해야함.
\(\rightarrow\) 버스의 사용 시간 & 쓰기 시간이 늘어나게 됨
지연 쓰기 (write-back)
-
캐시 메모리에만 써두었다가, 나중에 한번에 반영!
-
장점: 메모리 접근 횟수를 줄일 수 있음!
( “즉시 쓰기” 방식보다 빠르다 )
-
한계점: 메모리와 캐시 메모리간의 일관성이 깨질 수도!
e) 캐시 신선도
캐시에 저장된 데이터가 실제 원본 데이터와 얼마나 최신 상태인지
즉, “캐시된 정보가 아직 유효한가?”를 판단하는 개념
캐시 신선도 유지하는 방법들
방법 | 설명 |
---|---|
TTL (Time-To-Live) | 일정 시간 지나면 캐시를 자동으로 무효화함 |
LRU (Least Recently Used) | 가장 오래 안 쓰인 캐시부터 제거 |
캐시 무효화(Invalidation) | 원본이 바뀌면 캐시도 강제로 업데이트함 |