Always Be Wise

가상화 - 메모리 가상화 :세그멘테이션 본문

컴퓨터 시스템/OSTEP

가상화 - 메모리 가상화 :세그멘테이션

bewisesh91 2022. 1. 25. 01:58
728x90

베이스, 바운드 레지스터를 사용하면 운영체제는 프로세스를 물리 메모리의 다른 부분으로 쉽게 재배치할 수 있다.

그런데 문제는 주소 공간을 물리 메모리에 재배치할 때, 스택과 힙 사이의 빈 공간이 물리 메모리를 차지한다는 것이다.

이는 물리 메모리를 낭비이다. 또한, 주소 공간이 물리 메모리보다 큰 경우 베이스, 바운드 레지스터 기법은 실행이 어렵다.

세그멘테이션

세그먼트는 특정 길이를 갖는 연속적인 주소 공간을 의미한다.

세그멘테이션이란 MMU에 하나의 베이스와 바운드 값이 존재하는 것이 아니라,

세그먼트마다 베이스와 바운드 값이 존재하여, 각 세그먼트를 물리 메모리의 각기 다른 위치에 배치하는 기법이다. 

아래 왼쪽 그림의 주소 공간을 물리 메모리에 배치하려고 한다고 가정하자.

각 세그먼트의 베이스와 바운드 쌍을 이용하면,아래 오른쪽 그림과 같이 독립적으로 물리 메모리에 배치할 수 있다.

이때, 16KB 전체가 물리 메모리에 할당되는 것이 아니라, 사용 중인 부분만 물리 메모리에 할당된다.

세그멘테이션 기법을 사용하여 물리 주소를 계산하는 과정을 살펴보자.

우선, 아래와 같이 3쌍의 베이스, 바운드 레지스터 집합이 필요하다. 

 

가상 주소 100은 상기 예에서 코드 세그먼트에 속한다.

해당 주소에 참조가 일어나면 하드웨어는 베이스 값(32KB)에 이 세그먼트의 오프셋(100번지)을 더한다.

그 결과, 물리 주소는 32KB + 100 = 32868이 된다. 오프셋의 크기가 2KB를 넘지 않기 때문에 해당 물리 주소는 유효하다. 

이제 가상 주소 4200을 살펴보자. 이는 힙 세그먼트에 속한다. 우선, 4200이 힙 세그먼트 안에서 몇 번째 바이트인지 확인해야 한다.

힙은 가상 주소 4KB(4096)에서 시작하기 때문에, 4200은 4200 - 4096 = 104번째 바이트가 된다.

이 오프셋을 베이스 값(34KB)에 더하면 물리 주소는 34KB + 104 = 34290이다. 오프셋의 크기가 3KB를 넘지 않기에 이는 유효한 참조다.

만약, 오프셋의 크기가 커서 세그먼트의 범위를 벗어난다면, 하드웨어는 운영체제에 트랩을 발생시킨다. 이것이 바로 세그먼트 폴트이다.

세그먼트 종류의 파악

하드웨어는 가상 주소가 어느 세그먼트를 참조하는지, 그리고 해당 세그먼트 안에서 오프셋은 얼마인지를 어떻게 알 수 있을까?

일반적인 접근법으로는 가상 주소의 최상위 비트 몇 개를 세그먼트 종류를 나타나는 데 사용하는 것이다.

상기 예에서는 3개의 세그먼트가 있었다. 이를 표현하기 위해서는 2비트(00 : 코드, 01 : 힙, 10 : 스택)가 필요하다.

 

최상위 2비트(01)는 참조하는 세그먼트의 종류(힙)를 알려준다. 하위 12비트(000001101000)는 세그먼트 내의 오프셋(104)이다.

오프셋에 베이스 레지스터 값을 더하여 하드웨어는 최종 물리 주소를 계산한다. 이때, 오프셋이 바운드보다 작으면 유효한 주소이다.

세그먼트의 문제점은 가상 주소 공간의 활용도를 제한한다는 것이다. 상위 두 비트를 사용한다는 말은 16KB 주소 공간이 4개의 조각,

즉 4KB 크기로 나뉜다는 말이다. 각 세그먼트는 최대 크기, 상기 예에서는 4KB로 제한된다.

스택의 표현 

스택은 다른 세그먼트들과 달리 반대 방향으로 확장된다. 위의 예에서는 물리 주소 28KB에서 시작하여 26KB까지를 차지하는 것이다.

스택을 표현하기 위해서는 다른 방식의 주소 변환이 필요하다. 사실 이는 간단한 하드웨어의 도움으로 해결할 수 있다.

예를 들어, 아래와 같이 베이스, 바운드 값뿐 아니라 세그먼트가 어느 방향으로 확장하는지 레지스터에 기록하면 된다. 

 

세그먼트 공유

메모리를 절약하기 위해 때로는 주소 공간들 간에 특정 메모리 세그먼트를 공유하는 것이 유용하다.

특히, 코드 세그먼트의 공유가 일반적이다. 공유를 지원하기 위해서 다시 한번 하드웨어를 사용할 수 있다.

예를 들어, 레지스터에 protection bit라는 추가적인 정보를 기록하는 것이다.

아래 예에서 protection bit 세그먼트를 읽거나 쓸 수 있는지 혹은 세그먼트의 코드를 실행시킬 수 있는지를 나타낸다. 

코드 세그먼트를 읽기 전용으로 설정하면 주소 공간의 독립성을 유지하면서도 여러 프로세스가 주소 공간의 일부를 공유할 수 있다.

 

그런데 protection bit를 사용하면 하드웨어 알고리즘이 수정되어야 한다.

가상 주소가 범위 내에 있는지 확인하는 것 이외에 특정 액세스가 허용되는지를 확인해야 한다.

운영체제 지원

세그멘테이션의 도입을 위해서는 운영체제가 몇 가지 문제를 해결해야 한다.

첫 번째는 문맥 전환이다. 문맥 전환 시 운영체제는 세그먼트 레지스터의 저장과 복원을 수행한다. 

각 프로세스는 자신의 가상 주소 공간을 갖기에 운영체제는 프로세스 실행 시 레지스터들을 올바르게 설정해야 한다.

두 번째는 세그먼트 크기의 변경이다. 예를 들어, malloc( ) 함수 호출 시, 힙에 빈 공간이 없다면 힙 세그먼트의 크기를 증가시켜야 한다.

이 경우 메모리 관리 라이브러리가 힙을 확장하기 위하여 시스템 콜을 호출할 것이다. 운영체제는 이 시스템 콜을 처리해야 한다.

마지막으로 미사용 중인 물리 메모리 공간의 관리이다. 주소 공간이 생성되면 운영체제는 비어있는 물리 메모리 영역을 찾아야 한다.

베이스, 바운드 레지스터 기법의 경우 물리 메모리는 프로세스가 탑재될 슬롯의 집합이라고 생각할 수 있었다.

그러나 세그멘테이션의 경우, 프로세스는 많은 세그먼트를 가질 수 있고, 각 세그먼트의 크기는 다를 수 있다.

문제는 외부 단편화가 발생할 수 있다는 것이다. 아래 왼쪽 그림의 상황에서 새로운 프로세스가 생성되어 20KB를 할당한다고 가정하자.

총 24KB의 빈 공간이 존재하기는 하지만 하나의 연속된 공간이 아니다. 따라서 운영체제는 20KB의 요청을 충족시킬 수 없다.

 

이 문제의 해결책 중 한 가지는 기존의 세그먼트를 정리하여 물리 메모리를 압축하는 것이다.

예를 들어, 운영체제는 현재 실행 중인 프로세스를 중단하고 그들의 데이터를 하나의 연속된 공간에 복사하여 큰 빈 공간을 확보할 수 있다.

하지만 세그먼트 복사는 메모리에 부하가 큰 연산이고, 상당량의 프로세서 시간을 사용하기에 비용이 많이 든다.

다른 방법은 최적 적합, 최악 적합, 최초 적합 및 버디 알고리즘 등의 빈 공간 리스트를 관리하는 알고리즘을 사용하는 것이다.

요약

세그멘테이션 기법은 시스템이 세그먼트 단위로 가상 주소 공간을 물리 메모리에 재배치하기 때문에 물리 메모리를 절약할 수 있다.

더욱이 세그멘테이션에 필요한 산술 연산은 쉽고 하드웨어 구현에 적합하기 때문에 속도가 빠르다. 

그러나 세그먼트의 크기가 일정하지 않기 때문에 앞서 살펴본 것과 같이 외부 단편화 문제가 발생할 수 있다.

또한, 세그멘테이션이 아직 드문드문 사용되는 주소 공간을 지원할 만큼 충분히 유연하지 못하다는 점이다.

예를 들어, 크기가 크지만 드문드문 사용되는 힙에 접근하기 위해서는 여전히 힙 전체가 물리 메모리에 존재해야 하기 때문이다.

이러한 문제점들을 극복하기 위한 새로운 해결책이 필요하다.

Comments