✅ 세그멘테이션
기존에 베이스 바운드 레지스터를 사용하여 주소 변환을 한다면, 스택과 힙 사이에 사용되지 않는 큰 공간이 존재하여 내부 단편화가 발생했다. 이러한 측면에서 볼 때 베이스와 바운드 방식은 유연성이 없다!
✅ 세그멘테이션: 베이스/바운드의 일반화
MMU 안에 오직 하나의 베이스와 바운드 쌍만 존재하는 것이 아니라, 주소 공간의 논리적인 세그멘트마다 베이스와 바운드 쌍이 존재한다. 세그멘트는 특정 길이를 가지는 연속적인 주소 공간이다. 우리가 기준으로 삼은 주소 공간에는 코드, 스택, 힙 세 종류의 세그멘트가 있다. 세그멘테이션을 사용하면 운영체제는 각 세그멘트를 물리 메모리의 각기 다른 위치에 배치할 수 있고, 사용되지 않는 가상 주소 공간이 물리 메모리를 차지하는 것을 방지할 수 있다.
가상 주소 100번지 - 코드
위 예시에서 가상 주소 100번지를 참조한다고 가정하자. 가상 주소 100번지는 코드 세그멘트에 속한다. 참조가 일어나면 하드웨어는 베이스 값에 이 세그멘트의 오프셋(이 경우 100)을 더해 물리 주소는 100 + 32KB 또는 32868이 된다. 그 후, 주소가 범위 내에 있는지 검사하고(100은 2KB보다 작다), 범위 내에 있을 경우 물리 메모리 주소 32868을 읽는다.
가상 주소 4200번지 - 힙
가상 주소 4200번지는 힙에 속한다. 가상 주소 4200을 힙의 베이스 (34KB)에 더하면 물리 주소 39016을 얻지만 이 주소는 올바른 물리 주소가 아니다! 먼저 힙 안에서의 오프셋, 즉 주소가 참조하는 바이트가 이 세그멘트의 시작으로부터 몇 번째 바이트인지를 얻어야 한다. 힙은 가상 주소 4KB(4096)에서 시작하기 때문에 오프셋 4200은 실제로는 4200-4096, 즉 104가 된다. 이 오프셋(104)을 베이스 레지스터의 물리 주소 (34KB)에 더해 원하는 결과 34920을 얻게 된다.
가상 주소 7KB - 잘못된 접근
만일 힙의 마지막을 벗어난 7KB와 같은 잘못된 주소를 접근하려고 한다면, 운영체제가 트랩을 발생시킨다. 아마도 문제의 프로세스를 종료시킬 가능성이 크다. 이것을 우리는 세그멘트 폴트 라고 부르기로 했다.!!
✅ 세그멘트 종류 파악
하드웨어는 변환을 위해 세그멘트 레지스터를 사용한다. 하드웨어는 가상 주소가 어느 세그멘트를 참조하는지, 오프셋은 얼마인지를 어떻게 알 수 있을까?
한 가지 일반적인 접근법, 가상 주소의 최상위 몇 비트를 기준으로 주소 공간을 여러 세그멘트로 나누는 것이다.
→ 우리의 예시는 3개의 세그멘트가 있으므로 상위 2비트가 필요하다. (위 예시의 주소공간의 크기가 16KB이므로 2^14이고, 총 14비트 주소 공간이 나오는 것이다.)
이 방법은 바운드 검사도 쉽게 만든다. 오프셋이 바운드보다 작은지의 여부만 검사하면 된다.
// 14 bit VA 중 상위 2 bit를 얻는다. segment = (virtualAddress & segmentMask) >> seg_shift // 12 bit shift // offset을 얻는다. offset = (virtualAddress & offsetMask) if (offset >= bounds[segment]) // 오프셋이 바운드를 넘음 (오류) RaiseException(PROTECTION_FAULT) else PhysAddr = Base[segment] | offset // 베이스에 오프셋을 더한다. Register = AccessMemory(PhysAddr)
→ 하지만 이 방법은 이 방법은 상위 2비트로 3개의 주소공간을 나타내기 때문에, 나머지 1/4은 사용이 불가능하다는 단점이 있다. 이 문제를 해결하기 위해 일부 시스템은 코드와 힙을 하나의 세그멘트에 저장하고 세그멘트 선택을 위해 1비트만 사용한다. 특정 주소의 세그멘트를 하드웨어적으로 파악하는 다른 방법들도 있다.
스택
지금까지 아주 중요한 스택은 다루지 않았다. 스택은 다른 세그멘트와 한 가지 아주 중요한 차이가 있다. 다른 세그멘트들과는 반대 방향으로 확장된다는 것이다!! 따라서, 다른 방식의 변환이 필요하다.
첫 번째로, 간단한 하드웨어가 추가로 필요하다. 베이스와 바운드 값뿐 아니라 하드웨어는 세그멘트가 어느 방향으로 확장하는지도 알아야 한다. 예를 들어, 하나의 비트를 사용하여 주소가 커지는 쪽(양의 방향)으로 확장하면 1, 작아지는 쪽(음의 방향)으로 확장하면 0으로 설정할 수 있다. 코드와 힙은 1이고, 스택은 0인 것이다.
스택 주소 변환 예시
위 예시에서 가상 주소 15KB에 접근하려고 한다고 가정하자. 이 주소는 물리 주소 27KB에 매핑되어야 한다. 이 가상 주소를 이진 형태로 바꾸면 11 1100 0000 0000이 된다. 상위 2비트는 스택 세그멘트를 나타내고, 이를 고려하면 1100 0000 0000 == 3KB의 오프셋이 남는다. 올바른 음수 오프셋을 얻기 위해(스택은 반대로 확장하기 때문에) 3KB에서 세그멘트의 최대 크기를 빼야 한다. 세그멘트의 최대 크기는 1111 1111 1111 == 4KB 이므로, 올바른 오프셋은 3KB - 4KB = -1KB이다. 이 음수 오프셋(-1KB)을 베이스(28KB)에 더하면 올바른 물리 주소 27KB를 얻게 된다. 바운드 검사는 음수 오프셋의 절대값이 세그멘트의 크기보다 작다는 것을 확인하여 계산할 수 있다.
공유 지원
메모리를 절약하기 위해 때로는 주소 공간들 간에 특정 메모리 세그멘트를 공유하는 것이 유용하다. 특히, 코드 공유가 일반적이며, 현재 시스템에서도 광범위하게 사용 중이다.
공유를 지원하기 위해, 하드웨어에 protection bit의 추가가 필요하다. 세그멘트마다 protection bit를 추가하여 세그멘트를 읽거나 쓸 수 있는지 혹은 세그멘트의 코드를 실행시킬 수 있는지를 나타낸다. 코드 세그멘트를 읽기 전용으로 설정하면 주소 공간의 독립성을 유지하면서도, 여러 프로세스가 주소 공간의 일부를 공유할 수 있다.
소단위 대 대단위 세그멘테이션
우리 예제의 대부분은 지금까지 소수의 세그멘트 (코드, 스택, 힙)만을 지원하는 시스템에만 주로 초점을 맞췄다. 이것을 대단위(Coarse-grained)라고 말한다. 비교적 큰 단위로 분할하기 때문이다.
일부 초기 시스템은 주소 공간을 작은 크기의 공간으로 잘게 나누는 것이 허용되었기 때문에, 소단위(Fine-grained) 세그멘테이션이라고 부른다. 많은 수의 세그멘트를 지원하기 위해서는 여러 세그멘트의 정보를 메모리에 저장할 수 있는 세그멘트 테이블 같은 하드웨어가 필요하다. 세그멘트 테이블을 이용하면 매우 많은 세그멘트를 손쉽게 생성하고 융통성 있게 세그멘트를 사용할 수 있었다.
운영체제의 지원
문맥 교환 시 운영체제는 세그멘트 레지스터의 저장과 복원을 해야한다. 각 프로세스는 자신의 가상 주소 공간을 가지며, 운영체제는 프로세스가 다시 실행하기 전에 레지스터들을 올바르게 설정해야 한다.
두 번째로, 미사용 중인 물리 메모리 공간의 관리가 필요하다. 새로운 주소 공간이 생성되면 운영체제는 이 공간의 세그멘트를 위한 비어있는 물리 메모리 영역을 찾을 수 있어야 한다. 이전에 우리는 각 주소 공간의 크기가 동일하다고 가정했다. 지금은 프로세스가 많은 세그멘트를 가질 수 있고, 각 세그멘트는 크기가 다를 수 있다.
일반적으로 생길 수 있는 문제는 물리 메모리가 빠르게 작은 크기의 빈 공간들로 채워진다는 것이다. 이 작은 빈 공간들은 새로이 생겨나는 세그멘트에 할당하기도 힘들고 기존 세그멘트를 확장하는 데에도 도움이 되지 않는다. 이 문제를 외부 단편화라고 부른다.
작은 빈 공간들을 정리하는 해결책 중 하나는 물리 메모리를 압축하는 것이다. 하지만 세그멘트 복사는 메모리에 부하가 큰 연산이기 때문에 비용이 많이 든다.
간단한 방법은 빈 공간 리스트를 관리하는 알고리즘을 사용하는 것이다. 빈 공간 관리 알고리즘은 할당 가능한 메모리 영역들을 리스트 형태로 유지한다. 최적 적합(best-fit), 최악 적합(worst-fit), 최초 적합(first-fit), 버디 알고리즘(buddy algorithm) 등을 포함하여 수백 개의 방식이 존재한다.
✅ 세그멘테이션 요약
장점
- 기존 베이스/바운드 방식의 내부 단편화를 해결한다.
- 하드웨어 구현에 적합하기 때문에 속도가 빠르다.
- 변환 오버헤드도 최소이다.
- 코드 공유의 장점도 발생한다.
단점
- 외부 단편화가 발생한다.
- 드문드문 사용되는 주소 공간을 지원하기엔 유연성이 떨어진다.
→ 가끔 사용되는 힙에 접근하려면 힙 전체 공간이 물리 메모리에 존재해야 한다.
Share article