#os #computerarchitecture # Physical Memory vs Virtual Memory 가상화*Virtualization*을 이해하기 위해서는 가상 메모리*Virtual Memory*, 그리고 가상 메모리와 대비되는 개념인 물리 메모리*Physical Memory*를 이해해야 한다. ## Physical Memory 물리 메모리*Physical Memory*는 메인보드에 박혀 있는 그 메모리의 실제 모습을 나타내며, 바이트 단위의 메모리 셀*cell*이 연속적으로 이어진 배열의 형태로 되어 있다. 각 셀은 0부터 시작하는 고유의 물리 주소*Physical Address*를 가지고 있으며, CPU는 이 주소 체계를 이용하여 메모리와 관련된 연산을 수행한다. ![[PhysicalAddressing.png]] 그러나 메모리는 여전히 싸지 않으며 과거에는 더욱 비쌌고, 기기마다 하드웨어 사양이 제각기 달라 하나의 코드로 여러 기기에서 동일하게 작동하는 프로그램을 만들기란 불가능에 가까웠다. 그래서 이 문제를 해결하기 위해 가상화*Virtualization*라는 해결책이 등장하였다. ## Virutal Memory 가상화라 불리는 이 해결책의 핵심은 단순하다. 모든 프로세스가 각자 충분한 공간을 사용할 수 있도록 하자는 것이다. 그런데 실제 메모리는 하나고 모든 프로세스가 같이 사용하는데 이게 어떻게 가능한 걸까? 그 비결은 바로 프로세스가 보는 메모리를 주소 공간*Address Space*로 추상화시켜 물리적 메모리와 다르게 보이도록 하는 것이다. ![프로세스의 관점에서 본 주소 공간](addressspace.png) 프로세스가 보는 메모리는 위와 같이 하나의 연속적인 공간이며, 프로세스가 사용하기에 충분한 크기를 가지고 있다. 실제로는 모든 프로세스가 하나의 물리 메모리를 나누어 사용하지만, 프로세스에게는 이렇게 충분한 크기의 연속적인 주소 공간을 혼자 사용하고 있다는 일종의 눈속임을 주는 셈이다. 이것이 바로 프로세스가 보는 메모리인 가상 메모리*Virtual Memory*다. 가상 메모리에 접근할 때는 가상 주소*Virtual Address*를 사용하는데, 이 주소는 가상 메모리를 기준으로 구성된 주소여서 같은 메모리에 대한 주소여도 물리 주소 공간*Physical Address Space*의 주소와 가상 주소 공간*Virtual Address Space*의 주소가 다르다. 그럼에도 불구하고 오늘날 컴퓨터에는 아무런 문제가 없는 이유는 컴퓨터 시스템 내부에 가상 주소와 물리 주소를 같이 사용하기 위한 설계가 적용되었기 때문이다. ## Address Translation 이 설계를 이해하려면 우선 가상 주소와 물리 주소가 어떻게 같이 사용되는지 알아야 한다. 가장 기본적인 방법은 아래와 같이 가상 주소를 물리 주소로 바꿔주는 메모리 관리 유닛*Memory Management Unit, MMU*를 전용 하드웨어로 구현하는 것이다. ![[VirtualAddress.png]] MMU의 역할은 가상 주소를 읽고 그 가상 주소와 연결된 물리 주소에 접근하는 것인데, 이를 위하여 MMU 내부에 페이지 테이블*Page Table*이 담겨 있다. 페이지 테이블에는 가상 주소와 물리 주소가 한 쌍으로 묶여 있고, 이 테이블을 통하여 가상 주소는 물리 주소로 바뀌어 메모리에 접근할 때는 언제나 물리 주소를 사용하게 된다. # Paging 이 시점에서 물리 주소 공간과 가상 주소 공간의 크기를 비교해보자. 일반적인 PC에 사용하는 물리 메모리는 32GB 정도면 많다는 소리를 듣는데, 일반적인 PC에 사용하는 CPU 아키텍처는 64비트다. 이는 우리가 사용하는 가상 주소 공간의 크기가 최대 $2^{64}$ 바이트라는 말인데, 이 크기는 32GB를 훌쩍 넘는 것은 물론이고 서버나 슈퍼컴퓨터에서도 잘 사용하지 않는 크기의 메모리다. 그렇다면 실제로는 프로세스가 메인 메모리 뿐만 아니라 SSD 같은 저장 장치에도 저장되어 있는 것이다. 프로세스가 실제로 저장된 모습을 이해하기 위해서는 소프트웨어의 시점과는 다르게 접근할 필요가 있다. 운영체제는 메모리 가상화*Memory Virtualization*를 통해 실제로는 곳곳에 흩어져 있는 프로세스를 하나의 통합된 주소 공간으로 파악하고, 이 과정에서 메모리를 가상 메모리(혹은 논리 메모리*Logical Memory*)와 물리적 메모리로 구분하게 되는데, 덕분에 오늘날 모든 프로그램은 프로세스의 시점, 즉 가상 메모리를 기반으로 작성되어 물리적 메모리를 고려할 필요가 없다. ![Paging: 가상 메모리와 물리 메모리의 관계](MemoryPaging.png) 이런 일을 가능하게 해준 것이 바로 페이징*paging*이다. 페이징은 물리 메모리를 고정된 크기의 단위인 프레임*frame*으로, 가상 메모리를 동일한 크기의 페이지*page*로 쪼개는 것부터 시작한다. CPU가 생성한 모든 주소는 이 페이지에 기반하며, 크게 페이지 넘버*Page Number*와 페이지 오프셋*Page Offset*으로 나눌 수 있다. 페이지 넘버는 페이지의 위치를 가리키고 페이지 오프셋은 페이지 내부의 위치를 의미한다. ![[PageAddress.png]] 여기에 더해 MMU에 페이지 테이블을 구현하면 기본적인 페이징을 구현할 수 있다. MMU에서 가상 주소를 물리 주소로 바꾸는 원리는 단순하게 페이지 테이블을 보고 페이지 넘버 p를 프레임 넘버*Frame Number* f로 바꾸는 것이다. 페이지 p에 대응하는 프레임 f는 위치만 다를 뿐 내부 구성은 동일하기 때문에 페이지 오프셋 d는 그대로 사용된다. ## Page Table 이제 페이지 테이블의 구조를 자세하게 뜯어볼 차례다. 페이지 넘버 p는 페이지 테이블의 인덱스를 가리키며 그 자리에는 p에 대응하는 프레임 넘버 f가 있다. ![[PageTable.png]] 페이지 테이블은 페이지 테이블 엔트리*Page Table Entry, PTE*의 배열로 이루어져 있으며, 각 PTE에는 자신이 참조하고 있는 페이지의 물리 주소가 저장되어 있다. 이 주소는 RAM 뿐만 아니라 디스크의 어떤 공간을 가리킬 수도 있다. ![[PageIndirection.png]] 만약 PTE가 RAM에 존재하는 어떤 페이지를 가리키고 있고, 그 페이지를 실제로 RAM에서 읽을 수 있다면 Page Hit이라 부른다. 반면, PTE에 페이지의 주소가 없거나 RAM에 없는 페이지를 가리키고 있다면 이를 Page Fault라 부르며, 이럴 경우 디스크에 있는 페이지를 RAM으로 가져온다. 만약 이미 RAM이 꽉 차 있다면, 내부 정책에 따라 디스크로 보낼 페이지 하나를 선택해 그 페이지가 있던 자리에 저장한다. 하드웨어 수준에서는 페이징과 주소 변환을 통하여 가상화를 지원하고 있으며, 운영체제 수준에서는 페이지에 대한 내부 정책이나 여분의 메모리 공간을 리스트로 관리하는 등 메모리 관리 기능과 예외 처리 코드로 가상화를 지원하고 있다. --- # 참고자료 - A. Silberschatz, G. Gagne & P. B. Galvin, _Operating System Concepts_, 10th ed. Wiley, 2018. - R. Arpaci-Dusseau & A. Arpaci-Dusseau, _Operating Systems: Three Easy Pieces_, 1.10. Arpaci-Dusseau Books, 2023. - R. E. Bryant & D. R. O’Hallaron, _Computer systems: a programmer’s perspective_, Third edition. Boston Amsterdam London: Pearson, 2016.