본 벨로그의 내용 및 이미지는 다음의 강의를 참고해서 작성이 되었으며, 틀린 내용이 있을 수도 있으니 너그럽게 봐주시면 감사하겠습니다:)
가상 메모리 시스템의 모든 프로세스는 물리 메모리와 별개로 자신이 메모리의 어느 위치에 있는지 상관없이 0번지부터 시작하는 연속된 메모리 공간을 가진다. 그리고 각 프로세스가 가진 가상 메모리 공간 (Virtual Memory Space)의 크기는 컴퓨터 시스템이 가진 물리 메모리의 최대 크기로 한정된다. 32bit 체계의 경우 최대로 표현할 수 있는 주소가 약 4GB이기 때문에 각 프로세스가 할당할 수 있는 가상 메모리의 최대 크기 역시 약 4GB이다.
그렇다면 32bit 시스템에서 10개의 프로세스가 있다면 최소 40GB의 물리적 메모리가 필요하다는 이야기 인데, 실제로 40GB가 다 필요한 경우가 많지는 않다. 하지만 운영체제의 메모리 관리자는 RAM과 스왑영역을 활용해 실제 프로세스가 동작하기 위한 물리적 메모리 공간을 확보해놓는다.
이전에 프로세스마다 가상 메모리 공간이 할당된다고 했는데, 이 말은 프로세스마다 0번지, 1번지라는 메모리 주소가 할당된다는 의미이다. 하지만 이 말이 각각 프로세스 1번과 프로세스 2번의 가상 메모리 주소 0번지가 참조하는 주소가 둘다 실제 물리 메모리 주소의 0번지의 주소가 아니라는 뜻이다. (프로세스마다 메모리 공간이 가상화/추상화 되어있다는 의미) 따라서 가상 메모리 시스템에서 메모리 관리자는 RAM과 스왑 영역을 합쳐서 실제 메모리의 물리 주소로 변환하는 작업을 수행해주어야 하는데 이러한 작업을 동적 주소 변환 (Dynamic Addresss Translation) 이라고 하며, 매핑 테이블을 통해 각 프로세스의 가상 메모리 공간이 물리 메모리의 세그먼트/페이지로 매핑이 된다.
이렇게 메모리를 가상화 되어서 관리했을때 가장 큰 장점은 관리적 용이성이 매우 좋아진다는 것이다. 예를 들어, 프로세스가 죽었을때 매핑 테이블을 참조해서 메모리를 회수하면 보다 다른 프로세스가 해당 메모리를 활용할 수 있게 되고 보다 자원을 효율적으로 사용할 수 있게 된다.
가상 메모리 공간 역시 물리적 메모리 공간과 마찬가지로 고정 분할 방식인 페이징 기법과 가변 분할 방식인 세그멘테이션 기법으로 구분지어서 관리된다. 두 관리 기법이 갖는 단점을 보완하기 위해 가상 메모리 시스템 역시 세그멘테이션-페이징 혼용 기법을 사용해 관리된다. 강의에서 페이징 기법을 위주로 다루었음으로 포스팅에서도 페이징 기법 중심으로 정리하였다.
위의 그림에서처럼 가상 메모리 공간을 고정된 크기로 나누어서 관리했을때 각각의 고정된 크기를 '페이지' 라고 하며, 각 페이지에 상응되는 물리적 메모리 공간의 고정된 공간을 '프레임' 이라고 부른다고 한다. 일반적으로 관리의 편리성을 위해 페이지와 프레임의 크기는 똑같이 맞춰준다고 하며 강의에 따르면 위도우의 경우 4KB로 페이지를 분할해 관리한다고 한다. 그림에서 invalid라고 표시되어 있는 부분은 해당 페이지가 스왑 영역에 있다는 의미라고 책에서는 서술되어 있다. (강의에서는 실제 물리적 메모리 공간에 메핑된 곳이 없는 경우 invalid가 나오고 오류 메세지가 뜬다고 설명하고 있다.)
페이징 기법에서의 주소 변환 작업을 살펴보면 다음과 같다.
위의 그림처럼 한 페이지/프레임의 크기를 10B로 나누었고, 각 페이지/프레임이 10개의 주소를 가진다고 하자. 프로세스가 30번지의 내용을 읽으려고 할 때의 주소 변환 과정은 아래와 같다.
이러한 주소 변환 과정을 정형화해보면, 페이징 기법에서는 가상 주소를 VA=<P,A>로 표현하는데, 여기서 VA는 가상 주소, P는 페이지, D는 페이지의 처음 위치에서 해당 주소까지의 거리를 의미하며 offset이라고 정의되기도 한다. 위의 그림을 보면 가상 메모리의 페이지 VA=<P,A>가 물리 메모리의 프레임 PA=<F,D>로 메핑이 되는데, 페이지와 프레임의 크기를 똑같이 나누었을 경우 특정 주소까지의 거리가 변하지 않기 때문에 D는 변하지 않는 것을 확인할 수 있다.
메모리 접근 권한은 메모리의 특정 번지에 지정된 데이터를 사용할 수 있는 권한으로 읽기(read), 쓰기(write), 실행(execute) 권한이 있다. 일반적인 데이터에는 읽기 및 쓰기 권한이 적용되고, 상수나 읽기 전용 파일에는 읽기 권한이 적용된다.
위의 그림은 프로세스의 영역별 메모리 접근 권한을 나타낸 것이다. 프로세스는 몸체에 해당하는 코드 영역, 프로세스가 사용하는 데이터를 저장하는 데이터 영역, 프로세스를 실행하는데 필요하는 스택 영역과 프로세스 제어 영역으로 구성된다.
메모리 접근 권한 검사는 가상 주소에서 물리주소로 변환이 일어날 때마다 시행된다. 위의 그림처럼 매핑 테이블에서 각 프레임번호마다 권한 비트가 있어서 해당 프레임이 읽기가 가능한 영역인지 읽기/실행이 모두 가능한 영역인지를 메핑과정에서 검사하게 된다. 이렇듯 변환과정에서 유용한 접근인지 확인하기는 하지만 사용자가 직접 권한을 바꿀 수 있다고 한다. 이와 관련해서 강의에서 Data Execution Prevention 이라는 개념을 추가로 설명해주는데, 이는 데이터가 담기는 영역에서 코드가 실행되는 것을 운영체제가 선제적으로 막는 개념이다. 동적할당된 메모리 영역에 특정 기계어가 실행되버리면 보안문제 같은 이슈가 발생하기 때문이다.
위의 그림에서 프레임마다 권한비트를 추가하면 페이지 테이블의 크기가 커지는 자원을 낭비하는 문제가 발생해 이를 해결하기 위해 세그멘테이션 테이블을 추가해 테이블의 크기를 줄이는 기법이 도입되었다고 한다.