OS - 하드웨어 기반 주소 변환

김인회·2021년 11월 3일
0

OS

목록 보기
5/5

멀티프로그래밍(다중프로세스)의 메모리

멀티프로그래밍 기법이 아직 적용되기 이전이었던 구시대의 컴퓨터 시스템에서 메모리란 운영체제와 그리고 단 한 개의 프로그램만이 존재하고 있는 공간이었다.

이 시절의 컴퓨터는 오로지 하나의 프로세스 작업만을 수행하면 됐었기 때문에 딱히 메모리 관리에 있어 어떤 복잡한 절차가 필요하지 않았다.

이후 멀티프로그래밍 기법이 대두되면서 사용자는 컴퓨터 시스템에게 병렬처리의 기능을 요구하기 시작했다. 컴퓨터 시스템은 이제 하나의 프로세스만을 다루는 것이 아니라 여러 개의 프로세스를 동시에 다룰 수 있어야 했다.

여러 개의 프로세스를 동시에 다루기 위해서 가장 먼저 해결해야 했던 고민은 바로 각각의 프로세스들이 사용할 메모리를 어떻게 처리해 줘야 할지에 대한 문제였다. 그리고 이러한 고민속에서 내놓은 초창기의 해답은 정말 직관적이고 무식했다.

초창기의 해답은 프로세스별로 메모리의 공간을 따로 마련하기보다는 그냥 메모리에 대한 사용권을 프로세스 별로 돌려쓰는 느낌으로 진행되었다.

즉 운영체제와 한 개의 프로그램만이 존재한다는 기존의 메모리 구조를 변경하지 않았고 이것을 그대로 사용한다. 단지 다른 프로세스로 작업 대상을 전환해야 할 때 이 메모리 정보를 통째로 하드에 보관해놓고, 다음 작업 프로세스의 메모리 정보를 하드에서 통째로 가져오는 식의 방법을 택한 것이다.

결국 시스템은 프로세스의 전환이 한 번 일어날 때마다 메모리의 전 영역을 복사해서 하드에 저장했고, 또다시 하드에서부터 새로운 메모리 내용을 읽어와 교체해 주어야만 했다. 문맥교환을 위해 말그대로 정말 모든 것을 교환해야 했었으므로 오버헤드의 비용이 너무나도 컸다.

이러한 극도로 비효율적인 프로세스 전환 과정을 고치기 위해서 차라리 근본적인 메모리 구조 자체를 바꿔보기로 했다.

즉 운영체제와 한 개의 프로그램만이 존재한다는 기존의 메모리구조를 버리고, 더 이상 단순히 한 개의 프로그램만이 올라가는 공간으로서가 아니라 여러 프로그램이 동시에 올라가 있는 공간으로서 메모리를 바라보기 시작한 것이다.

이 방식은 확실히 효율적이었다. 작업 프로세스를 전환하기 위해서 하드디스크와 매번 소통하며 자료를 주고받지 않아도 되었고 단지 CPU에게 바라봐야 할 적절한 메모리의 주소를 알려주기만 하면 됐다.(물론 하드디스크와 완전히 소통하지 않아도 된다는 소리는 아니다. 많은 프로세스를 메모리에 미리 올려놓음으로써 최대한 하드디스크와의 접점을 줄였다는 소리이다)

하지만 세상일이라는 게 원하는 대로 마냥 쉽게만 흘러가지는 않는다.

하나의 통합된 메모리 공간에 여러 개의 프로세스를 동시에 올리는 방식은 확실히 성능적인 면에서 기존의 방식보다 매우 우수했지만, 프로세스들끼리 서로의 메모리 영역을 침범할 수 있다는 불안정성의 문제가 새로 나타난 것이다.

이러한 상황 속에서 이제 시스템은 어떻게 해야 각 프로세스들의 투명성을 보장해 줄 수 있을까라는 고민을 하기 시작한다. (각 프로세스의 메모리 영역 보호에 대한 고민)

즉 메모리 가상화라는 방법론이 등장하게 된 배경이 마련된 것이다.

일반적인 메모리의 구조

(오른쪽 Physical Memory가 실제 시스템 측면에서 바라본 메모리의 단면이고, 왼쪽 0KB부터 16KB(바운드)까지 존재하는 메모리 영역은 프로그램의 측면에서 바라본 메모리의 단면이다)

메모리 가상화

메모리 가상화는 프로세스에게 추상화된 거짓 환상을 심어준다.

가상화된 메모리 환경에서 프로세스는 오로지 자신만이 시스템의 전 메모리 영역을 다루고 있고 다른 프로세스는 존재하지 않는 것처럼 생각하게 된다.

마치 멀티프로그래밍 이전 구시대의 컴퓨터 시스템처럼 말이다.

물론 실제 시스템 환경에서의 메모리에는 운영체제나 다수의 여러 프로세스들이 모두 한자리씩 자리를 차지하고 있다.

이러한 시스템 메모리의 실제 환경을 속이고(물리주소를 감추고) 각 프로세스에게 별도의 가상주소만을 건네주어(0번지부터 시작해서 할당된 지점까지 영역을 갖는) 마치 프로세스가 오로지 자신만이 메모리 공간을 독점해서 사용하고 있는 중이라고 착각을 하게 만드는 것이 바로 메모리 가상화이다.

가상화된 메모리영역에서 프로그램은 오로지 자신의 눈에 보이는 메모리주소만을 다루기만 하면 될 뿐이다.

실제 데이터가 저장되는 메모리의 물리주소가 어떻게 되는지, 특정 메모리 주소를 사용하는 데 있어서 충돌과 같은 문제가 발생하지는 않을지, 기타 여러가지 메모리와 관련된 자질구질한 고민들은 프로그램이 하지 않는다.

그러한 짜증나는 고민들은 다른 시스템(운영체제, 하드웨어)에게 던져버리면 된다.

프로그램 입장에서 복잡하게 생각할 것 없이 자신은 눈에 보이는 가상화된 메모리영역만 이용하겠다는 사고방식은 굉장히 큰 메리트가 된다.

프로그램 설계 시 메모리와 관련된 복잡한 부분들은 전혀 신경 쓰지 않아도 된다는 것은, 유동적으로 변하고 있는 시스템 메모리 상황을 전혀 예측할 필요 없이 오로지 필요한 기능의 구현에만 포커스를 유지해도 된다는 것을 의미하기 때문이다.

주소 변환 (베이스와 바운드 아이디어 - 동적재배치)

하드웨어 기반 주소 변환이라고도 부르는 주소변환 기법은 운영체제가 하드웨어(CPU)의 도움을 받아서 가상화된 메모리의 주소를 실제 주소로 변환하는 기법이다.

이러한 주소변환 기법을 구현한 대표적인 아이디어로는 베이스와 바운드 개념을 이용한 동적재배치 기술이 있다.

동적재배치는 스케줄링되고 있는 각각 프로세스들에게 베이스와 바운드라는 개별적인 상태 값을 지니게 하고 해당 값을 이용하여 주소변환을 수행한다.

베이스와 바운드라는 상태값은 특별하게도 CPU가 특정 레지스터(CPU의 MMU의 일부)에 별도로 공간을 따로 마련하여 관리하고 있는 상태값이다.

(이것은 시스템의 가장 중추부라고 할 수 있는 CPU가 주소변환에 직접적으로 관여하고 있다는 것을 뜻한다)

CPU는 베이스와 바운드라는 이 2개의 특별한 상태값을 이용해 프로세스의 가상주소를 실제주소로 변환(맵핑)한다.

변환의 원리는 정말로 간단하다.

프로세스의 가상주소에 베이스 상태값을 더하면 본래의 물리주소 값이 나오는 방식이다. (Virtual Address + BASE Register = Physical Address)

바운드는 해당 프로세스가 사용할 수 있는 메모리 영역의 최대치를 나타내는 값으로서, 특정 범위를 벗어나는 불법적인 메모리 참조가 일어날 때 예외를 발생시키기 위해 이용되는 검사값이다.

즉 간단하게 요약하자면 베이스가 바로 메모리의 START 포인터이고, 바운드는 메모리 END 포인터로서 이 두 포인터가 감싸고 있는 내부의 메모리 공간이 바로 프로세스가 사용하고 있는 실제 물리 메모리 공간이라고 생각하면 될 것 같다.

이렇게 베이스와 바운드라는 간단한 개념만을 이용해도 메모리 가상화를 훌륭하게 이뤄낼 수 있다. 운영체제와 하드웨어가 합작하여 하나의 통합된 메모리안에서도 여러 프로세스들이 안전하게 활동할 수 있도록 시스템을 제어해 주는 것이다.

CPU는 각 프로세스들의 베이스값을 통해 본래의 물리주소가 어디있는 지 찾아낼 수 있고, 바운드값을 통해 서로 간의 영역을 안전하게 보호해 준다.

(이러한 방식의 한 가지 아쉬운 점은 프로세스별로 주어진 가상화된 주소공간의 크기가 가변적이지 않고 항상 고정되어 있다는 것이다. 사용되지 않는 스택과 힙영역으로 인해 사용처 없이 놀고 있는 내부의 빈 메모리공간들이 프로세스 별로 계속 발생한다. 이런 식으로 낭비되는 공간을 내부 단편화라고 부른다)

추가적으로 알아두면 좋은만한 것들

  1. 하드웨어가 주소변환에 직접 개입하는 것이 아니라 소프트웨어 단에서 모든 주소변환을 처리하는 것을 정적 재배치라고 부른다. 정적재배치는 운영체제가 변환할 주소를 고르고, 주소변환마저도 직접 처리하기 때문에 메모리영역에 대한 보호정책을 세우는 것이 까다롭다. 동적재배치에서는 운영체제가 프로그램이 사용하는 가상주소를 고르고(전해받고), 다시 더 보안권한이 높은 하드웨어에게 해당주소를 넘겨주는 것으로 하드웨어에게 주소변환과 주소 적합여부에 대한 판단과정을 맡기게 된다. 만약 하드웨어가 주소변환을 진행하면서 해당 주소가 부적합한 주소라고 판단되는 경우가 발생한다면 곧바로 운영체제에게 예외처리를 콜하여 해당 프로그램은 즉시 종료시킬 것이므로 메모리영역을 안전하게 보호할 수 있다. 즉 동적재배치에서는 더 높은 보안권한을 갖는 하드웨어를 검사과정에 이용하기때문에 주소변환을 안전하게 처리하는 것이 가능하다.

  2. 메모리관리 API 함수 -> malloc(), free() 함수는 대표적인 메모리관리 라이브러리 함수(유저모드)이다. 내부적으로 brk라는 시스템콜(커널모드)을 사용해서 메모리를 관리한다.

  3. sizeof() 함수의 경우 함수이지만 연산자로 간주되는 게 맞다. sizeof는 컴파일시간에 인자에 들어온 자료의 크기로 변환되어 컴파일러에게 처리되기 때문이다. (함수의 호출은 런타임시 일어나는 과정이다)

  4. 프로세스 상태 워드 : CPU의 레지스터 영역으로 프로세스의 상태와 관련된 정보들이 담긴다. 커널모드, 유저모드를 나타내는 비트가 존재하는 곳이기도 하다.

  5. 프로세스 구조체 == 프로세스 제어블록(PCB) : 운영체제가 관리하는 프로세스 별 자료구조. 해당 자료구조 안에서 베이스와 바운드 상태값을 보관하고 나중에 CPU가 읽어가게 한다. CPU는 해당값을 읽어 다시 자신의 레지스터에 보관한다.

profile
안녕하세요. 잘부탁드립니다.

0개의 댓글