가상 메모리
실제 각 프로세스마다 충분한 메모리를 할당하기에는 메모리 크기가 한계가 있음.
(리눅스는 하나의 프로세스가 4기가를 차지함)
적은 메모리에서 여러 프로세스를 다루기 위해 등장한 개념이 가상 메모리!
cpu가 한 프로세스의 모든 영역을 사용하지는 않는다,
프로세스가 모두 4기가 씩을 부여 받더라도, 정작 사용하는 공간은 제한적이다.
사용할 영역만 RAM에 저장하여 실행하는 방식으로 하면 메모리가 부족해도 여러 프로세스를 작동 시킬 수 있다.
가상 메모리의 기본 아이디어
프로세스는 가상 주소를 사용하고, 실제 해당 주소에서 데이터를 읽고 쓸 때만 물리 주소로 바꿔주자.
- virtual address : 프로세스가 참조하는 주소
- physical address : 실제 메모리 주소
MMU(Memory Managemnet Unit)
- cpu에 코드 실행 시, 가상 주소 메모리 접근이 필요할 경우, 해당 주소를 물리 주소 값으로 변환하는 하드웨어.
- 주소 변환을 빠르게 하기 위해 하드웨어 장치를 사용.
cpu는 가상 메모리를 다루고, 실제 해당 주소 접근 시 MMU가 물리 메모리 접근
페이징 시스템(paging system)
- 크기가 동일한 페이지로 가상 주소 공간과 이에 매칭하는 물리 주소 공간을 관리
- 하드웨어 지원 필요(intel x86(32bit)는 4kb, 2mb, 1gb 지원)
- 리눅스에서는 4kb로 paging
가상 메모리를 페이징 단위에 맞게 잘게 쪼개서 페이징 번호를 부여하고,
page table에 해당 페이징에 맞는 물리 주소 매핑 정보를 저장한다.
4gb짜리 프로세스와 PCB에 page table 구조체를 가리키는 주소가 존재해서
cpu가 어떤 가상 메모리 주소에 접근하면 해당 페이징에 맞는 page table에 접근해서,
매핑 되어 있는 물리 주소를 얻어 물리 주소로 접근하는 방식이다.
내부 단편화 : 4kb씩 데이터를 나눈다고 했을 때 만약 1kb가 남으면…? 1kb도 4kb크기의 페이지로 할당.(공간 낭비)
페이징 시스템 구조
1. page 혹은 page fram: 고정된 크기의 block
가상 주소 v = (p, d)
- p: 가상 메모리 페이지 번호
- d: p안에서 참조하는 위치(변위 혹은 오프셋이라고도 부름)
(페이지 안에서도 내가 접근할 위치의 값에 접근할 수 있도록 하는 것이 d)
(즉 어떤 페이지의 시작 주소가 있을 텐데 그 페이지에서 d 만큼 내려오면 우리가 원하는 주소)
2. page table: 물리 주소에 있는 페이지 번호와 해당 페이지의 첫 물리 주소 정보를 매핑한 표
(CR3라는 레지스터에 page table 시작 주소가 담겨져 있다.)
- 해당 프로세스의 page table에 해당 가상 주소가 포함된 page번호가 있는 지 확인
- page 번호가 있으면 이 page가 매핑된 첫 물리주소가 p’이 됨
- page 번호가 있어도 해당 page가 램에 저장되지 않았을 수 있음(필요 없다고 생각해서)
- p’+d 가 실제 물리 주소가 됨
다중 단계 페이징 시스템
32bit 시스템에서 한 프로세스가 4gb(리눅스 기준)인데,
이를 4kb 기준으로 다 잘라서 다 테이블에 저장하려면 너무 비효율적이다.
따라서 페이징 정보를 단계를 나누어 생성하자.
즉 필요없는 페이지는 생성하지 말자!
가상 주소를 page directory, page table, offset으로 만들어서
필요 없는 페이지들은 페이지 테이블을 안 만들도록 할 수 있다~!
MMU와 TLB(컴퓨터 구조)
한번 가상 주소를 통해 얻은 물리 주소를 TLB라는 캐쉬에 저장해놓는다.
만약 다시 동일한 주소를 요청하면, 해당 테이블을 찾아보지 않아도 TLB를 통해 얻을 수 있다.
이를 통해 메모리에서 접근하고 값을 찾는 과정을 줄일 수 있다.
페이징 시스템과 공유 메모리
프로세스 간 동일한 물리 주소를 가르킬 수 있다.
즉 서로 다른 프로세스 a, b의 내용이 같은 영역은 물리주소로 변환할 때 같은 물리 주소로 줄 수 있다.
엥? 서로 한 프로세스가 데이터를 변경할 수 있잖아요..?
그렇다. 하지만 생각해보자. 만약 a 영역에서 물리주소를 얻어 데이터를 변경하려고 한다면?
그때 메모리 상에 다른 물리주소를 복사해서 그 주소를 b에게 주고, 원래 물리 주소를 요청대로 수정하면 된다.
이런 방식을 통해 각 프로세스가 1기가 정도 점유하고 있는 커널 공간을 모두 같은 물리 주소로 변환해,
용량 낭비를 줄일 수 있다.
프로세스 생성 시간도 줄어들고, 공유 공간을 물리 메모리를 공유로 효율적으로 쓸 수 있다.
요구 페이징(demanding paging, demanded paging)
프로세스 모든 데이터를 메모리에 담지 않고, 실행 중 필요한 시점에만 메모리로 적재
더 이상 필요하지 않은 페이지 프레임은 다시 저장매체에 저장(페이지 교체 알고리즘 필요)
(선행 페이징이 이와 반대되는 개념. 모두 메모리에 적재.)
페이지 폴트 인터럽트(page fault interupt)
어떤 페이지가 물리 메모리에 없을 때 발생하는 인터럽트.
운영체제는 이 인터럽트를 받으면, 해당 페이지를 물리 메모리에 올리고 다시 작업을 실행시킨다.
페이지 폴트는 인터럽트이므로, 앞서 배웠던 인터럽트 처리과정을 동일하게 발생시킨다.
스레싱(Thrashing)
- 페이지 폴트가 자주 일어나면, 시간이 오래 걸린다.
- 반복적으로 페이지 폴트가 발생해, 과도하게 페이지 교체작업이 일어나 실제로는 아무 일도 진행 안되는 상태.
- 자주 쓰일 것 같은 페이지는 미리 메모리에 올려 놓는게 좋다.(알고리즘)
페이지 교체 정책(page replacement policy)
물리 메모리가 가득 찼는데 특정 페이지를 물리 메모리에 올려야 할 때
어떤 페이지를 선택해 빼고 넣으려고 했던 페이지를 넣는다.
FIFO 알고리즘
- 가장 먼저 들어온 페이지가 나가자!
OPT 알고리즘
- 오랫동안 안 쓸 페이지를 내리자!(일반적인 OS에서는 예측 불가)
LRU 알고리즘
- 가장 오래 전에 사용한 페이지가 나가자!
LFU 알고리즘
- 지금까지 가장 적게 쓰인 페이지가 나가자!
NUR 알고리즘
- 각 페이지마다 참조 비트, 수정 비트를 (R, M)으로 표현하여
- (0,0)->(0,1)->(1,0)->(1,1) 로 우선순위로 나가라!
세그멘테이션 기법
페이징과 비교해서 기억하는 정도만 보자.
가상 메모리를 서로 크기가 다른 논리적 단위인 세그먼트로 분할
(페이징은 일관된 크기로 분할)
- 세그먼트 가상 주소
v = (s,d) s: 세그먼트 번호, d: 블록 내 세그먼트 범위 - 외부 단편화 : 세그먼트의 크기가 들어갈만한 연속적인 물리 공간이 없으면 문제가 생김.(크기가 지 맘대로다 보니…)