저번 글에서, 연속 메모리 할당의 문제점에 대해 알아보았다.
연속 메모리 할당은 외부단편화와, 실제 물리 메모리보다 큰 프로세스를 실행할 수 없다는 단점들이 있다.
실행하고자 하는 프로세스의 일부만 메모리에 적재함으로써, 물리 메모리보다 훨씬 큰 프로세스를 적재할 수 있는 기술이다.
가상 메모리를 관리하는 기법에는 페이징과 세그멘테이션이 있다.
외부 단편화가 발생하는 근본적인 문제는, 각기 다른 크기의 프로세스가 메모리에 연속적으로 할당되었기 때문이다.
만약, 메모리에 적재되는 프로세스들이 모두 일정한 크기를 가지고 있다면?
외부 단편화는 발생하지 않을 것이다.
그렇다면 모든 프로세스를 동일한 크기로 자르고, 메모리에 불연속적으로 할당할 수 있다면?
이 역시 외부 단편화는 발생하지 않을 것이다. 이 방법이 페이징이다.
참고로, 페이징에서도 스와핑이 가능하다.
프로세스 단위의 스왑 인, 스왑 아웃대신 페이지 단위의 페이지 인, 페이지 아웃이라는 용어를 사용한다.
메모리에 적재될 필요가 없느 페이지들을 내쫒고, 실행에 필요한 페이지들을 들여보낸다.
프로세스를 실행하기 위해, 모든 페이지가 꼭 메모리에 적재되어야 할 필요는 없다.
→ 물리 메모리보다 큰 프로세스도 실행될 수 있다.
그런데, 페이징은 프로세스 조각들이 불연속적으로 할당되어있는 것인데, CPU가 여기 저기 흩어져있는 그들의 위치를 어떻게 확인할 수 있을까?
이런 식의 문제점들이 있다.
이러한 문제점들을 해결하기 위한 방법이 또 있다.
물리 주소(실제 메모리 내 주소)에 불연속적으로 배치되더라도, 논리주소(CPU가 바라보는 주소)에는 연속적으로 배치되도록 하는 방법.
페이지 번호와 프레임 번호를 짝지어주는 일종의 이정표!
프로세스들마다 페이지 테이블이 존재한다.
아래 사진에서, 현재 0번 페이지는 3번 프레임, 1번 페이지는 5번 프레임에 할당되어 있다는 의미.
다시 말해, 물리적으로는 흩어져서 저장되어 있더라도 CPU 입장에서는 그냥 연속적으로 보이게 할 수 있다. CPU는 논리 주소를 순차적으로 실행하기만 하면 됨!
페이징은 외부 단편화를 해결하기 위한 방법이지만, 내부 단편화라는 문제점을 일으키기도 한다 ..
어떤 프로세스의 크기는 꼭 페이지 크기의 배수가 아닐 수 도 있다.
만약 페이지의 크기가 10KB, 프로세스는 108KB라면,
마지막의 페이지에서는 8KB만을 사용하여, 2KB만큼이 남게 된다.
즉 이렇게 낭비되는 현상을 내부 단편화라고 한다.
물론 이런 경우, 내부 단편화로 낭비되는 크기는 페이지 크기보다 작기 때문에 외부 단편화보다는 적게 낭비된다.
하편 위에서 프로세스들마다 페이지 테이블을 가지고 있다고 하는데, 이들은 어디에 저장되는 걸까?
CPU 내 프로세스 테이블 베이스 레지스터(PTBR)는 각 프로세스의 페이지 테이블이 적재된 주소를 가리킨다.
아래 예시를 보자.
CPU가 프로세스 A를 실행하고 있다. 프로세스 A를 이루고 있는 페이지 테이블을 찾아야한다. 이들은 PTBR을 통해 알 수 있다.
몬가 이상하다.
페이지 테이블들이 메모리에 모두 저장되어 있다는 뜻인데...
페이지 테이블을 참조하기 위해 한 번, 페이지를 참조하기 위해 한번. 즉 메모리 접근 시간이 두 배로 늘어난다.
그래서 더 효율적인 방법이 또 있다.
TLB는 CPU 곁의 페이지 테이블의 캐시 메모리이다.
TLB에는 자주 참조하고 있는 페이지 테이블의 일부가 저장되고, 굳이 메모리의 페이지 테이블에 접근하지 않아도 페이지와 프레임 번호에 대한 정보를 가져올 수 있다. 이렇게 불필요한 메모리 접근을 줄일 수 있다.
CPU가 접근하려는 논리 주소가 TLB에 있다면? TLB히트 -> 메모리 한 번 접근
CPU가 접근하려는 논리 주소가 TLB에 없다면? TLB미스 -> 메모리 두 번 접근
특정 주소에 접근하려면 어떤 정보가 필요할까?
를 알아야 한다.
페이징 시스템에서의 논리 주소는 페이지 번호와 변위로 이루어진다.
내가 접근하고자 하는 페이지의 번호
접근하고자 하는 주소는 그 페이지에서 얼만큼 떨어져 있는 지에 대한 정보
페이지의 변위는 프레임의 변위와 같다. 페이지와 프레임의 크기는 같기 때문 !
예를 들어 논리주소, 즉 <페이지 번호,변위> 가 <5,2>인 곳에 접근하려고 한다.
5번 페이지 번호에서 2만큼 떨어진 곳을 찾으면 된다.
5번 페이지를 확인해보면, 프레임 번호 1과 매치된다. 즉 1번 프레임으로 가 보면 8번지-12번지를 의미하고, 8번지에서 2만큼 떨어진 곳은 10번지이다.
CPU가 결과적으로 접근하게 될 물리주소는 10번지이다.
페이지 테이블에 담기는 정보는 페이지 번호, 프레임 번호 이외에도 부가적인 정보가 조금 더 담긴다. 그것들에 대해 알아보자.
유효 비트
현재 해당 페이지에 접근이 가능한지 여부를 알려주는 정보이다.
유효 비트가 1이라면 메모리에 적재되어 있는 페이지이고, 유효비트가 0이라면 적재되지 않은 페이지를 의미(스왑 영역에 존재)한다.
만약 유효비트가 0인 페이지에 접근하려고 하면?
페이지 폴트(page fault) 인터럽트 발생
1. CPU는 기존 작업 내역을 백업한다.
2. 페이지 폴트 처리 루틴을 실행한다.
3. 페이지 처리 루틴은 원하는 페이지를 메모리로 가져온 뒤, 유효 비트를 1로 변경한다.
4. 페이지 폴트를 처리했다면 이제 CPU는 해당 페이지에 접근할 수 있게 된다.
보호 비트
페이지의 보호를 위해 존재하는 비트.
보호 비트가 0이면 읽기 전용, 보호 비트가 1이면 읽기 및 쓰기 전용으로 설정할 수 있다.
보호 비트를 3가지 두어 읽기/쓰기/실행을 설정할 수도 있다.
참조 비트
CPU가 이 페이지를 접근한 적이 있는 지의 여부를 기록한다.
수정 비트 (dirty bit)
CPU가 이 페이지에 데이터를 쓴 적이 있는 지의 여부를 기록한다.
수정 비트가 존재하는 이유는 스와핑과 관련이 있다.
페이지가 메모리에서 사라질 때, 보조 기억장치에 쓰기를 해야할 지 말 지 여부를 판단한다.
만약 쓰기 작업 했다면 수정된 페이지가 스왑 아웃될 때 보조 기억장치에도 쓰기 작업을 해야한다. 이를 수정 비트를 확인하여 판단한다.