컴퓨터 프로그래밍 공부/컴퓨터 구조 + 운영체제

가상 메모리 - 연속 메모리 할당

게임 개발 2024. 4. 18. 12:38
연속 메모리 할당

 

 프로세스에 연속적인 메모리 공간을 할당하는 방식을 연속 메모리 할당 방식이라고 한다.

프로세스들을 메모리에 연속적으로 할당할 때 무엇을 고려해야 하는지,

그리고 어떤 잠재적인 문제가 있는지 알아보자.

 

 

스와핑

 

 메모리에 적재된 프로세스들 중에는 현재 실행디지 않는 프로세스가 있을 수 있다.

입출력 작업의 요구로 대기 상태가 된 프로세스라던지,

오랫동인 사용되지 않은 프로세스가 이런 프로세스들에 속한다.

이러한 프로세스들을 임시로 보조기억장치 일부 영역으로 쫒아내고,

그렇게 해서 생긴 메모리상의 빈 공간에 도 다른 프로세스를 적재하여 실행하는 방식을 스와핑이라고 한다.

 

이때 프로세스들이 쫒겨나는 보조기억장치의 일부 영역을 스왑 영역이라고 한다.

그리고 현재 실행되지 않는 프로세스가 메모리에서 스왑 영역으로 옮겨지는 것을 스왑 아웃,

반대로 스왑 영역에 있던 프로세스가 다시 메모리로 옮겨오는 것을 스왑 인이라고 한다.

스왑 아웃 되었던 프로세스가 다시 스왑 인될 때는

스왑 아웃되기 전의 물리 주소와는 다른 주소에 적재될 수 있다.

 

 

스와핑을 이용하면 프로세스들이 요구하는 메모리 주소 공간의 크기가 실제 모미리 크기보다 큰 겨웅에도

프로세스들을 동시 실행이 가능하다.

 

 

 

메모리 할당

 

프로세스는 메모리 내의 빈 공간에 적재되어야 한다.

메모리 내에 빈 공간이 여러 개 있다면 프로세스를 어디에 배치해야 할까?

비어 있는 메모리 공간에 프로세스를 연속적으로 할당하는 방식을 알아보자.

 

여기에는 대표적으로 최초 적합, 최적 적합, 최악 적합의 세 가지 방식이 있다.

이는 다음 그림과 같은 예시가 있다고 가정하자.

 

 

 

 

 

최초 적합

 

최초 적합은 운영체제가 메모리 내의 빈 공간을 순서대로 검색하다가 적재할 수 있는 공간을 발견하면

그 공간에 프로세스를 배치하는 방식이다.

즉 운영체제가 빈 공간 A > 빈 공간 B > 빈 공간 C 순으로 빈 공간을 검색했다면

프로세스는 빈 공간 A에 적재된다.

최초 적합 방식은 프로세스가 적재될 수 있는 공간을 발견하는 즉시 메모리를 할당하는 방식이므로

검색을 최소화할 수 있고 결과적으로 빠른 할당이 가능하다.

 

 

최적 적합

 

최적 적합은 운영체제가 빈 공간을 모두 검색해 본 후,

프로세스가 적재될수 있는 공간 중 가장 작은 공간에 프로세스를 배치하는 방식이다.

앞선 예시에서 프로세스가 적재될 수 있는 빈 공간 중 가장 작은 공간은 빈 공간 C이다.

그렇기에 최적 적합 방식으로 메모리를 할당하면 프로세스는 빈 공간 C 에 할당이 된다

 

 

최악 적합

 

최악 적합은 운영체제가 빈 공간을 모두 검색해 본 후,

프로세스가 적재될 수 있는 공간 중 가장 큰 공간에 프로세스를 배치하는 방식이다.

앞선 예시에서 프로세스가 적재될 수 있는 빈 공간 중 가장 큰 공간은 빈 공간 B이다.

그렇기에 최악 적합 방식으로 메모리를 할당하면 프로세스는 빈 공간 B에 할당된다.

 

 

외부 단편화

 

 

프로세스는 메모리에 연속적으로 배치하는 연속 메모리 할당은

언뜻 들으면 당연하게 느껴질 수 있지만,

사실 이는 메모리를 효율적으로 사용하는 방법이 아니다.

왜냐하면 연속 메모리 할당은 외부 단편화라는 문제를 내표하고 있기 때문이다.

외부 단편화가 무엇이며 왜 발생하는가에 대해 알아보자.

 

아무런 프로세스도 적재되지 않은 상태의 메모리 전체를 그려보면

오른쪽 그림과 같이 표현할 수 있다.

운영체제 영역에는 운영체제가 적재되어 있고,

사용자 영역에는 어떠한 프로세스도 적재되어 있지 않다.

 

 

이제 사용자 영역에 하나둘씩 프로세스들이 적재되는 상황을 그려보자.

편의를 위해 사용자 영역 크기는 200MB 라고 가정해보자.

이를 간단히 생각해보면 아래와 같이 적재할 수 있다.

여기서 프로세스 B와 D의 실행이 끝났다고 가정해보자.

이 프로세스들은 더 이상 메모리에 남아있을 필요가 없다.

그래서 빈 공간이 생기게 된다고 가정하자.

 

현재 메모리 공간에 남아 있는 총 메모리 량은 50MB이지만,

50MB의 크기만큼 메모리를 적재할 수 없다.

빈 공간의 총합이 메모리 크기와 같거나 크더라도,

어느 빈 공가넹도 50MB 크기의 프로세스가 적재될 수 없기 때문이다.

 

프로세스들이 메모리에 연속적으로 할당되는 환경에서는

위와 같이 프로세스들이 실행되고 종료되그를 반복하며

메모리 사이 사이에 빈 공간들이 생기게 된다.

프로세스 바깥에 생기는 이러한 빈 공간들은 분명 빈 공간이지만,

그 공간보다 큰 프로세스를 저갲하기 어려운 상황을 초래하고,

결국 메모리 낭비로 이어진다.

이러한 현상을 외부 단편화라고 한다.

 

 

그림고 같은 경우의 예시는 메모리에 프로세스가 몇 개 없는 간단한 가정으로

외부 단편화가 큰 문제가 아닌 듯 느끼기 십상이다.

실제로는 이보다 메모리 용량도 크고 적재되는 프로세스도 많기 때문에

외부 단편화로 인해 낭비되는 공간은 더욱 크다.

그렇기에 외부 단편화 문제는 반드시 해결해야하는 문제이다.

 

외부 단편화를 해결할 수 있는 대표적인 방안으로 메모리를 압축하는 방법이 있다.

메모리 조각 모음이라고도 부르며, 압축은 여기저기 흩어져 있는 빈 공간들을

한로 모으는 방식으로 메모리 내에 저장된 프로세스를 적당히 재배치시켜

여기저기 흩어져 있는 작은 빈 공간을 하나의 큰 빈 공간으로 만드는 방법이다.

 

다만 이러한 압축 방식은 여러 단점이 있다.

작은 빈 공간들을 하나로 모으는 동안 시스템은 하던 일을 중지해야 하고,

메모리에 있는 내용을 옮기는 작업은 많은 오버헤드를 야기하며,

어떤 프로세스를 어떻게 움직여야 오버헤드를 최소화하며 압축할 수 있는지에 대한

명확한 방법을 결정하기 어렵다.

 

이에 외부 단편화를 없앨 수 있는 또 다른 해결 방안이 등장했는데,

이것이 오늘날까지도 사용되는 가상 메모리 기법, 그 중에서도 페이징 기법이다.

 

 

페이징을 통한 가상 메모리 관리

 

 

 페이징은 현대 운영체제 메모리 관리 기법에 있어 가장 중요한 개념이라고도 해도 과언이 아니다.

페이징이 왜 생겨났으며 어떤 원리로 작동 하는지 이해해보자.

 

가상 메모리란?

 가상 메모리는 실행하고자 하는 프로그램을 일부만 메모리에 적재하여

실제 물리 메모리크기보다 더 큰 프로세스를 실행할 수 있게 하는 기술이다.

이를 가능케 하는 가상 메모리 관리 기법에는 크게 페이징 세그멘테이션이 있지만,

현재 대부분의 운영체제가 사용하는 페이징 기법을 다뤄보자.

 

 

 

페이징이란

 연속 메모리 할당 방식에서 외부 단편화가 생긴 근본적인 이유는

각기 다른 크기의 프로세스가 메모리에 연속적으로 할당되었기 때문이다.

 

예를 들어 메모리공간과 프로세스들을 10MB 단위의 일정한 크기로 자르고,

잘린 메모리 조각들에 프로세스들을 불연속적으로 적재할 수 있다면

외부 단편화는 발생하지 않는다.

이것이 페이징 기법이다.

 

페이징은 프로세스의 논리 주소 공간을 페이지라는 일정한 단위로 자르고,

메모리 물리 주소 공간을 프레임이라는 페이지와 동일한 크기의 단위로 자른 뒤

페이지를 프레임에 할당하는 가상 메모리 관리 기법이다.

 

페이징에서도 스와핑을 사용할 수 있다.

페이징을 사용하는 시스템에서는 프로세스 전체가 스왑 아웃 / 스왑 인되는 것이 아닌

페이지 단위로 스왑 아웃 / 스왑 인이된다.

즉, 메모리에 적재될 필요가 없는 페이지들은 보조기억장치로 스왑 아웃되고,

실행에 필요한 페이지들은 메모리로 스왑 인 되는 것이다.

 

페이징 시스템에서의 스왑 아웃은 페이지 아웃, 스왑 인은 페이지 인이라고 부르기도 한다.

 

 

 

혼자 곻부하는 컴퓨터 구조 + 운영체제 출저

 

 

이를 다르게 말하면 한 프로세스를 실행하기 위해

프로세스 전체가 메모리에 적재될 필요는 없다는 말과 같다.

프로세스를 이루는 페이지 중 실행에 필요한 일부 페이지만을 메모리에 적재하고,

당장 실행에 필요하지 않은 페이지들은 보조기억장치에 남겨둘 수 있다.

이와 같은 방식을 통해 물리 메모리보다 더 큰 프로세스를 실행할 수 있다.

 

 

 

 

 

페이지 테이블

 

 

하지만, 신경써야 할 맹점이 하나 있다.

프로세스가 메모리에 불연속적으로 배치되어 있다면,

CPU입장에서는 이를 순차적으로 실행할 수 가 없다.

 

프로세스를 다루는 페이지가 어느 프레임에 적재되어 있는지 CPU가 모두 알고 있기란 어렵기 때문이다.

즉, 프로세스가 메모리에 불연속적으로 배치되면 CPU 입장에서는

'다음에 실행할 명령어 위치'를 찾기가 어려워진다.

 

이를 해결하기 위해 페이징 시스템은

프로세스가 비록 실제 메모리 내의 주소인 물리 주소에 불연속적으로 배치되더라도

CPU가 바라보는 주소인 논리 주소에는 연속적으로 배치되도록 페이지 이블을 이용한다.

 

페이지 테이블은 페이지 번호와 프레임 번호를 짝지어 주는 일종의 이정표이다.

CPU로 하여금 페이지 번호만 보고 해당 페이지가 적재된 프레임을 찾을 수 있게 한다.

다시 말해 페이지 테이블은 현재 어떤 페이지가 어떤 프레임에 할당되었는지를 알려준다.

 

 

프로세스마다 각자의 프로세스 테이블이 있다.

가령 프로세스 A의 페이지 테이블이 아래와 같다면 CPU 는 이를 보고 

'0번 페이지는 3번 프레임에,

1번 페이지는 5번 프레임에, 2번 페이지는 2번 프레임에 할당되어 있다'라는 사실을 알 수 있다.

 

이러한 방식으로 비록 물리 주소상에서는 프로세스들이 분산되어 저장되어 있더라도

CPU 입장에서 바라본 논리 주소는 연속적으로 보일 수 있다.

즉 프로세스들이 메모리에 분산되어 저장되어 있더라도

CPU는 논리 주소를 그저 순차적으로 실행하면 된다.

 

 

페이징 기법에서 유의할 것

 

페이징은 외부 단편화 문제를 해결할 수 있지만,

내부 단편화라는 문제를 야기할 수 있다.

 

페이징은 프로세스의 논리 주소 공간을 페이지라는 일정한 크기 단위로 자른다고 했다.

 

그런데 모든 프로세스가 페이지 크기에 딱 맞게 잘리는 것은 아니다.

다시 말해 모든 프로세스 크기가 페이지의 배수는 아니라는 것이다.

 

가령 페이지 크기가 10KB이고 프로세스 크기는 108KB라고 해보자.

이 경우 마지막 페이지는 2KB만큼의 크기가 남게된다.

이러한 메모리 낭비를 내부 단편화라고 한다.

 

내부 단편화는 하나의 페이지 크기보다 작은 크기로 발생한다.

그렇기에 하나의 페이지 크기가 작다면 발생하는 내부 단편화의 크기는 작이질 것으로 기대할 수 있다.

 

하지만 하나의 페이지 크기를 너무 작게 설정하면 그만큼 페이지 테이블의 크기도 커지기 때문에

페이지 테이블이 차지하는 공간이 낭비하게 된다.

 

즉 내부 단편화를 적당히 방지하면서

너무 크지 않은 페이지 테이블이 만들어지도록 페이지의 크기를 조정하는 것이 중요하다.

 

각 프로세스마다 각자의 프로세스 테이블을 가지고 있고,

각 프로세스의 페이지 테이블들은 메모리에 적재되어 있다.

 

그리고 CPU 내의

페이지 테이블 베이스 레지스터(PTBR) 각 프로세스의 페이지 테이블이 적재된 주소를 가리키고 있다. 

 

그런데 이렇게 페이지 테이블을 메모리에 두면 문제가 있다.

메모리 접근 시간이 두 배로 늘어난다는 것이다.

그렇게 알게 된 프레임에 접근하기 위해 한 번, 이렇게 총 두 번의 메모리 접근이 필요하다.

 

이와 같은 문제를 해결하기 위해 CPU 곁에 (일반적으로 MMU 내에) 

TLB라는 페이지 테이블의 캐시 메모리를 둔다.

우리가 사용하는 컴퓨터 CPU 곁에는 TLB가 있다.

TLB는 페이지 테이블의 캐시이기 때문에 페이지 테이블의 일부 내용을 저장한다.

참조 지역성에 근거해 주로 최근에 사용된 페이지 위주로 가져와 저장한다.

 

CPU가 발생한 논리 주소에 대한 페이지 번호가 TLB에 있을 경우 이를 TLB 히트 라고 한다.

이 경우에는 페이지가 적재된 프레임을 알기 위해 메모리에 접근할 필요가 없다.

즉 메모리 접근을 한 번만 하면 된다.

 

만일 페이지 번호가 TLB에 없을 경우,TLB 미스라고 한다.

어쩔 수 없이 페이지가 적재된 프레임을 알기 위해 메모리 내의 페이지 테이블에 접근하는 수밖에 없다.

 

 

 

페이징에서 주소 변환

 

하나의 페이지 혹은 프레임은 여러 주소를 포괄하고 있다.

그렇기에 특정 주소에 접근하려면 아래와 같은 두 가지 정보가 필요하다.

 

 

1. 어떤 페이지 혹은 프레임에 접근하고 싶은지

2. 접근하려는 주소가 그 페이지 혹은 프레임으로부터 얼마나 떨어져 있는지

 

그렇기에 페이징 시스템에는 모든 논리 주소가 기본적으로 페이지 번호와 변위로 이루어져 있다.

가령 CPU가 32비트 주소를 내보냈다면,

이 중 N비트는 페이지 번호, 32-N 비트는 변위, 이런 식으로 말이다.

 

페이지 번호는 말 그대로 접근하고자 하는 페이지 번호이다.

페이지 테이블에서 해당 페이지 번호를 찾으면 어떤 프레이멩 할당되었는지를 알 수 있다. 

 

변위는 접근하려는 주소가 프레임의 시작 번지로부터 얼만큼 떨어져 있는지를 알기 위한 정보이다.

즉, 논리 주소 <페이지 번호, 변위>는 페이지 테이블을 통해 물리 주소 <프레임 번호, 변위>로 변환한다.

 

 

 

 

 

 

 

 

위 과정을 말보다 예제를 통해 이해하는 것이 더욱 빠르다.

가령 CPU와 페이지 테이블 메모리 상태가 현재 아래와 같다고 해보자.

하나의 페이지/프레임이 네 개의 주소로 구성되어 있는 간단한 상황을 가정해보자

 

위의 매핑 테이블을 페이지 테이블이라고 생각해보면 간단하다.

 

CPU가 4번 페이지, 변위 2라는 논리 2에 접근하고 싶어한다고 가정해보자.

CPU가 접근하게 될 물리 주소는 어디일까? 페이지 테이블을 보면

4번 페이지는 현재 V(물리 주소 8)이라는 벨류로 저장되어 있다.

그렇다면 CPU는 8번지 물리주소에 접근하게 된다.

 

페이지 테이블 엔트리

 

페이지 테이블을 조금 자세히 들여다보게 되면

페이지 테이블의 각 엔트리, 다시 말해 페이지 테이블의 각각의 행들을 페이지 테이블 엔트리라고 한다.

 

페이지 테이블 엔트리에는 페이지 번호, 프레임 번호 외에도 다른 중요한 정보들이 있다.

대표적인 것이 유효비트, 보호 비트, 참조 비트, 수정 비트이다.

 

유효 비트

 

 유효 비트는 현재 해당 페이지에 접근 가능한지 여부를 알려준다.

페이지 테이블 엔트리에서 프레임 번호 다음으로 중요한 정보라고도 볼 수 있다.

페이징에서도 스와핑을 사용할 수 있다고 언급한 적이 있다.

일반적으로 프로세스를 이루는 모든 페이지가 메모리에 있지 않다.

일부 페이지는 보조기억장치(스왑 영역)에 있는 경우가 많다.

유효 비트는 현재 페이지가 메모리에 적재되어 있는지 아니면 보조기억장치에 있는지 알려주는 비트이다.

 

즉, 페이지가 메모리에 적재되어 있다면 유효 비트가 1,

페이지가 메모리에 적재되어 있지 않다면 유효 비트가 0이 된다.

 

 

 

만일 CPU가 유효 비트가 0인,

메모리에 적재되어 있지 않은 페이지로 접근하려고 하면 어떻게 될까?

이 경우는 페이지 폴트라는 예외가 발생한다.

 

CPU가 페이지 폴트를 처리하는 과정은 하드웨어 인터럽트를 처리하는 과정과 아주 유사하다.

 

1. CPU는 기존의 작업 내역을 백업한다.

2. 페이지 폴트 처리 루틴을 실행한다.

3. 페이지 처리 루틴은 원하는 페이지를 메모리로 가져온 뒤 유효 비트를 1로 변경해 준다.

4. 페이지 폴트를 처리했다면 이제 CPU는 해당 페이지에 접근할 수 있게 된다.

 

즉, 유효 비트는 해당 페이지가 메모리에 적재되어 있는지 여부를 알려주는 비트로,

페이지가 메모리에 적재되어 있지 않다면 페이지 폴트가 발생한다.

 

 

보호 비트

 

 보호 비트는 페이지 보호 기능을 위해 존재하는 비트이다.

보호 비트를 통해 해당 페이지가 읽고 쓰기가 가능한 페이지인지,

혹은 읽기만 가능한 페이지인지를 나타낼 수 있다.

가령 비트가 0일 경우 이 페이지는 읽기만 가능한 페이지임을 나타내고,

1일 경우 읽고 쓰기가 모두 가능한 페이지임을 나타내는 것이다.

앞서 프로세스를 이루는 요소 중 코드 영역은 읽기 전용 영역이라고 설명한 적이 있다.

이러한 읽기 전용 페이지에 쓰기를 시도하면 운영체제가 보호 비트를 읽어온 뒤 이를 막아준다.

이와 같은 방식들로 보호비트는 페이지의 읽고쓰는 방식의 규칙을 정해주어 보호한다.

 

 

 

보호 비트는 세 개의 비트로 조금 더 복잡하게 구현할 수도 있다.

읽기를 나타내는 r, 쓰기를 나타내는 w, 실행을 나태느는 x의 조합으로

읽기, 쓰기, 실행하기 권한의 조합을 나타낼 수 있다.

가령 보호 비트가 100으로 설정된 페이지의 경우 r은 1, w와 x는 0이므로 이 페이지는 읽기만 가능하고,

보호 비트가 110으로 설정된 페이지의 경우 이 페이지는 읽고 쓰기만 가능하고 실행은 불가능하다.

그리고 111으로 설정된 페이지는 읽기, 쓰기, 실행이 모두 가능하다.

 

 

참조 비트

 

 참조 비트는 CPU가 이 페이지에 접근한 적이 있는지 여부를 나타낸다.

적재 이후 CPU가 읽거나 쓴 페이지는 참조 비트가 1로 세팅되고,

적재 이후 한 번도 읽거나 쓴 적이 없는 페이지는 0으로 유지된다.

 

즉 참조한 적이 있는지 없는지를 체크해주는 비트이다.

 

수정 비트

 

 수정 비트는 해당 페이지에 데이터를 쓴 적이 있는지 없는지 수정 여부를 알려준다.

다른말로는 더티 비트라고도 불린다.

 

이 비트가 1이면 변경된 적이 있는 페이지,

0이면 변경된 적이 없는 페이지 ( 한 번도 접근한 적 없거나 읽기만 했던 페이지)임을 나타낸다.

 

 

 

수정 비트가 존재하는 이유는 뭘까?

 

수정 비트는 페이지가 메모리에서 사라질 때 보조기억장치에 쓰기 작업을 해야 하는지,

할 필요가 없는지를 판단하기 위해 존재한다.

 

CPU는 메모리를 읽기도 하지만 메모리에 값을 쓰기도 한다.

 

CPU가 한 번도 접근하지 않았거나 읽기만 한 페이지의 경우 보조기억장치에 저장된 해당 페이지의 내용과

메모리에 저장된 페이지 내용은 아래 그림과 같이 서로 같은 값을 가지고 있다.

 

이렇게 한 번도 수정된 적이 없는 페이지가 스왑 아웃될 경우 아무런 추가 작업 없이

새로 적재된 페이지로 덮어 쓰기만 하면 된다.

 

어짜피 똑같은 페이지가 보조기억장치에 저장되어 있으니까 말이다.

 

 

하지만 CPU가 쓰기 작업을 수행한 페이지(수정 비트가 1인 페이지)의 경우

보조기억장치에 저장된 페이지의 내용과 메모리에 저장된 페이지의 내용은 서로 다른 값을 가지게 된다.

 

 

즉 수정 비트는 해당 페이지가 수정된 적이 있는지를 나타내는 것이다.

 

 

페이징의 이점 - 쓰기 시 복사

 

외부 단편화 문제를 해결한다는 점 이외에도 페이징이 제공하는 이점은 다양하다.

대표적인 것이 프로세스 간에 페이지를 공유할 수 있다는 점이다.

프로세스 간 페이지를 공유하는 사례로는 공유 라이브러리 등 다양하지만,

대표적인 예시로 쓰기 시 복사가 있다.

 

멀티프로세스의 개념 중에서 프로세스를 fork하여 동일한 프로세스 두 개가 복제되면

코드 및 데이터 영역을 비롯한 모든 자원이 복제되어 메모리에 적재되는 이론이 있다.

그러고는 프로세스를 통째로 메모리에 중복 저장하지 않으면서 프로세스끼리 자원을 공유하지 않는 방법도 있다.

 

유닉스와 리눅스와 같은 운영체제에서는 fork 시스템 호출을 하면

부모 프로세스의 복사본이 자식 프로세스로서 만들어진다.

프로세스 간에는 기본적으로 자원을 공유하지 않는다는 프로세스의 전통적인 개념에 입각하면

새롭게 생성된 자식 프로세스의 코드 및 데이터 영역은

부모 프로세스가 적재된 메모리 공간과 전혀 다른 메모리 공간에 생성된다.

 

한 마디로 부모 프로세스의 메모리 영역이 다른 영역에 자식 프로세스로서 복제되고,

각 프로세스의 페이지 테이블은 자신이 고유한 페이지가 할당된 프레임을 가리킨다.

하지만 이 복사 작업은 프로세스 생성 시간을 늧출 뿐만 아니라 불필요한 메모리 낭비를 야기한다.

 

반면 쓰기 시 복사에서는 부모 프로세스와 동일한 자식 프로세스가 생성되면

자식 프로세스로 하여금 부모 프로세스와 동일한 프레임을 가리키게된다.

이로써 굳이 부모 프로세스의 메모리 공간을 복사하지 않고도

동일한 코드 및 데이터 영역을 가리킬 수 있다.

만일 부모 프로세스와 자식 프로세스가 메모리에 어떠한 데이터도 쓰지 않고

그저 읽기 작업만 이어나간다면 이 상태는 지속된다.

 

그런데 프로세스 간에는 자원을 공유하지 않는다고 했다.

부모 프로세스 혹은 자식 프로세스 둘 중 하나가 페이지에 쓰기 작업을 하면

그 순간 해당 페이지가 별도의 공간으로 복제된다.

각 프로세스는 자신의 고유한 페이지가 할당된 프레임을 가리키는 것이다.

 

이것이 쓰기 시 복사이다.

 

이런 쓰기시 복사를 통해 프로세스 생성 시간을 줄이는 것은 물론 메모리 공간 절약도 가능하다.

 

 

 

계층적 페이징

 

페이지 테이블의 크기는 생각보다 작지 않다.

프로세스의 크기가 커지면 자연히 프로세스 테이블의 크기도 커지기 때문에

프로세스를 이루는 모든 페이지 테이블 엔트리를 메모리에 두는 것은 큰 메모리 낭비이다.

이에 프로세스를 이루는 모든 페이지 테이블 엔트리를 항상 메모리에 유지하지 않을 수 있는 방법이 등장했다.

이것이 계층적 페이징이다.

 

그럼 계층적 페이지란 무엇일까?

 

계층적 페이징은

페이지 테이블을 페이징하여 여러 단계의 페이지를 두는 방식이다.

여러 단계의 페이지를 둔다는 점에서 다단계 페이지 테이블이라고도 한다.

 

프로세스의 페이지 테이블을 여러 개의 페이지로 자르고,

바깥쪽에 페이지 테이블을 하나 더 두어 잘린 페이지 테이블의 페이지들을 가리키게 하는 방식이다.

 

말이 조금 헷갈릴 수도 있는데,

간단히 말하자면,

한 프로세스의 페이지 테이블이 다음과 같다고 가정해보자.

 

이 페이지 테이블을 여러 개의 페이지로 쪼개고,

이 페이지들을 가리키는 페이지 테이블을 두는 방식이 계층적 페이지이다.

 

페이지 테이블을 이렇게 꼐층적으로 구성하면

모든 페이지 테이블을 항상 메모리에 유지할 필요가 없다.

페이지 테이블들 중 몇 개는 보조기억장치에 있어도 무방하며,

추후 해당 페이지 테이블을 참조해야 할 때 있으면 그때 메모리에 적재하면 그만이다.

막대한 크기의 페이지 테이블로 인해 낭비되는 공간을 줄일 수있다.

 

다만, CPU와 가장 가까이 위치한 페이지 테이블(Outer 페이지 테이블)은 항상 메모리에 유지해야 한다.

 

계층적 페이징을 사용하는 경우 CPU가 발생하는 논리 주소도 달라지게 되며,

계층적 페이징을 사용하지 않을 경우 논리 주소는 앞서 학습했듯이 다음과 같은 형태로 만들어진다.

 

 

하지만 계층적 페이징을 이용하는 환경에서의 논리 주소는 아래와 같은 형태로 만들어진다.

바깥 페이지 번호에 해당하는 항목은

CPU와 접근한 곳에 위치한(바깥에 위치한) 페이지 테이블 엔트리를 가리키고,

안쪽 페이지 번호는 첫 번재 페이지 테이블 바깥에 위치한 두 번째 페이지 테이블,

즉 페이지 테이블의 페이지 번호를 가라킨다.

 

 

이러한 논리 주소를 토대로 주소 변환은 다음과 같이 이루어진다.

 

1. 바깥 페이지 번호를 통해 페이지 테이블의 페이지를 찾기

2. 페이지 테이블의 페이지를 통해 프레임 번호를 찾고 변위를 더함으로서 물리 주소 얻기

 

 

위는 바깥과 안쪽으로 구성된 페이지 테이블(2단계 페이징) 예시를 들었으나

페이지 테이블의 계층은 세 개, 네 개, 그 이상의 계층으로도 구성될 수 있다.

다만 페이지 테이블의 계층이 늘어날수록 페이지 폴트가 발생했을 경우 메모리 참조 회수가 많아지므로

계층이 많다고 해서 반드시 좋다고 볼 수는 없다.