가상메모리(Virtual Memory)는 컴퓨터가 사용하는 메모리 관리 테크닉이다.

실제메모리를 추상화하여 가상 메모리에 올림으로써 프로세스들이 연속된 물리적 메모리 공간처럼 여기게 만들 수 있을 뿐만 아니라 실제 메모리보다 많은 크기의 메모리도 운용이 가능하다.


가상메모리 기술이 동작하기 위해서는 2가지 이슈가 있다.

하나는 가상 메모리를 물리 메모리에 매핑시키는 Address Translation이고, 다른 하나는 이 가상 공간에 대한 관리 이슈이다.


이러한 Virtual Memory 와 Physical Memory 의 매핑을 위한 기술로 Paging 기법이 사용된다.


페이징 기법은 컴퓨터가 메인 메모리에서 사용하기 위해 데이터를 저장하고 검색하는 메모리 관리 기법이다.

페이징 기법을 통해 컴퓨터의 물리적 메모리는 연속적으로 할당되어 존재할 필요가 없으며, 반대로 연속적으로 존재하지 않는 물리적 메모리라도 페이징 기법을 통해 연속적으로 존재하는 것처럼 이용될 수 있다.


페이징 방식에서는 가상 메모리 상의 주소 공간을 일정한 크기로 분할한다. 

주소공간은 페이지 단위로 나뉘어져있으며 실제 기억공간은 페이지 크기와 같은 프레임으로 나누어 사용한다.


 - Frame : 물리 메모리를 일정된 한 크기로 나눈 블록

 - Page : 가상 메모리를 일정된 한 크기로 나눈 블록


이 때 Frame 과 Page 의 크기는 동일하게 관리된다.

페이지의 크기는 크기는 시스템에 따라 다르며 크기가 작으면 메모리를 단편화하기 쉬워지지만 페이지의 갯수가 많아지기 때문에 페이지 단위의 입출력이 자주 발생하게 된다.


페이지가 하나의 프레임을 할당받으면, 물리 메모리에 위치하게 된다.

프레임을 할당받지 못한 페이지들은 외부저장장치에 저장되며, 마찬가지로 프레임과 같은 크기 단위로 관리된다.


페이징 기법에서 페이지는 페이지테이블(Page Table) 이라는 자료구조 형태로 관리된다.

Page Table 은 프로세스의 페이지 정보를 저장하고 있으며, 하나의 프로세스는 하나의 페이지 테이블을 가진다.

Page table 은 Index 를 키로 해당 페이지에 할당된 메모리(Frame)의 시작 주소를 Value 로 저장하고 있다.


페이지 테이블 엔트리(Page Table Entry)는 페이지 테이블의 레코드를 말한다.

PTE 의 각 필드에는 다음 내용들이 기록된다.


 - 페이지 기본 주소(Page base address)


 - 플래그 비트(Flag bit)

  - Accessed bit : 페이지에 대한 접근이 있었는지를 나타낸다.

  - Dirty bit : 페이지의 내용에 변경이 있었는지를 나타낸다.

  - Present bit : 현재 페이지에 할당된 프레임이 있는지를 나타낸다.

  - Read/Write bit : 읽기/쓰기에 대한 권한을 표시한다.


페이지의 크기는 하드웨어에 의해 정의되며 대게 2의 제곱으로 증가한다.



페이징 기법에서 동적 주소 변환 과정은 다음과 같다.


(1) 수행 중인 프로세스가 가상주소를 참조한다.


(2) 페이징 기법을 통해 페이지 p가 페이지 프레임 f에 있음을 알아낸다.


(3) 실주소 r = f(페이지 프레임) + d(오프셋) 를 구해낸다.


위와 같은 방식으로 페이지 테이블은 Virtual Memory 에서 Physical Memory 를 참조가능하도록 지원한다.



페이지 테이블은 사용을 위해서 메모리에 존재해야 하지만, 그렇게 되면 메모리 접근에 있어서 중복 호출이 일어나므로(페이지 테이블에 한번, 페이지테이블을 통한 실제 메모리에 한번), 대게 MMU 의 지원을 받아 매핑시키게 된다. 



(참고 : https://www.geeksforgeeks.org/operating-system-paging/

https://gabrieletolomei.wordpress.com/miscellanea/operating-systems/virtual-memory-paging-and-swapping/)



RAII는 C++ 진영에서 자주 쓰이는 Idiom으로 자원의 안전한 사용을 위해서 객체가 쓰이는 Scope를 벗어나면 자원을 해제해주는 기법이다. 메모리 누수를 관리하는 효과적인 기법이며, 제대로만 사용하면 오히려 별도의 관리 모듈이 붙는 것보다 안정적이다.


이는 C++ 에서 자원을 얻을 때 초기화가 되어야 하며, 유효한 객체 형태가 반환되어야하는 것을 의미한다. 반대로 객체가 사라질 때에는 가진 자원을 전부 반환해야 하며 객체가 유효하지 않은 형태가 되어야 한다.


 그리고 이것은 C++ 의 메모리 누수를 관리하는 철학이며, 이를 Scope 단위로 관리하고자 한다.


 그렇기 때문에 C/C++ 과 같은 통칭 .NET 등에서 Unmanaged 라 불리는 언어들을 다룰 때에는 메모리를 사용하는 각 변수들의 유효한 Scope 를 정확히 파악하는 것이 아주 중요하다.


 heap의 자원은 명시적 해제 이전에 해제되지 않지만 Stack 변수의 경우 Scope를 벗어나면 Destructor가 호출되는 것을 이용한 것이다.


 Exception 등 Control Flow Breaking 시에도 RAII를 이용하여 자원관리를 할 수 있으며 C++의 제작단계에서 스트로스트룹이 finally를 굳이 고안하지 않은 이유이기도 하다.



물론 이러한 특성들이 많은 개발자들의 C++의 진입 장벽을 높이는데에 포인터와 같이 큰 몫을 차지하고 있기 때문에 C++11 등 모던 C++에서는 여러 가지 장치들을 통해 메모리를 관리해주려는 노력을 하고 있다.


'Programming Language > C&C++' 카테고리의 다른 글

Stdafx.h 란?  (0) 2018.09.25
[Old] C++Type Casting 정리  (0) 2018.09.25

+ Recent posts