[Operating System/Windows/Virtual Memory] 운영체제의 메모리 관리(feat.윈도우)

SHark·2025년 1월 23일
0
post-thumbnail

저번 글은 운영체제와 컴퓨터 구조 전반에 걸쳐 가상메모리 시스템에 대한 개념을 잡은 시간이라면, 이번 글은 더 구체적인(?) 구현체인 윈도우(OS)가 어떤식으로 메모리를 관리하는지 볼겁니다.
Application을 만든 경험이 있다면 공감하실 내용인데, 누구든 이론(계획)은 잘 정립한 후, 시작을 합니다. 하지만, 구현체인 프로그램들은 제 생각보다 신기한 것이 되어있을 때가 많죠.

OS는 이론을 정말 깔끔하고 잘 정립이 되어있습니다.(저보다 뛰어난 엄청난 박사/교수님들이 토론을 끊임없이하며 정립된 이론이죠) 그럼에도 불구하고, 구현하는 과정에서 디테일한 차이점들이 생기기 마련입니다. 따라서, 구현체인 OS들(각종 리눅스,윈도우..등)을 공부하다보면 이론과 꽤 다르게 느껴질 수 있기 때문에, 내가 OS를 만들어본다(?)고 생각하면서 읽으면 더 이해가 잘 되지 않을까 싶습니다.

실제로 우리가 사용하는 것은 구현체 이므로.. 구현체에 대한 디테일도 챙기는건 나쁘지 않습니다. 현업자들의 시선에 보기에는 이론적인 배경보다는 이런 디테일적인 부분들이 더 중요할테니까요.

네, 결론은 저희는 둘 다 공부를 해야한다는 소리를 좋게 이야기하는거죠?

OS가 해야하는 일

운영체제 입장에서 메모리를 위해 해야하는 일은 무엇이 있을지 생각해봅시다. CPU가 가상메모리 시스템을 채택하기로 하였고, 가상 메모리 시스템에 대한 이해도 인정했습니다. 가상 메모리 시스템에서 일어난 일들을 정리해볼까요?

프로그램의 크기는 상관이 없어졌다

필요한 페이지들을 프로세스에게 그때그때 주고, 나머지들은 Disk에 저장을 해야합니다. 그렇다면, 해당 상황(Page Out이라고 합니다)을 OS에서 어떻게 처리할지 정의가 되어있어야 합니다.

  • PageOut된 Page가 필요한 경우
  • PageOut의 기준

OverAllocation
가상 메모리 시스템은 프로그램 입장에서는 실제로 해당 Ram만큼의 크기가 없는데, 있는 것처럼 속이기 때문에 프로그램들은 모두 OverAllocation이 되었다고 표현하기도 합니다. 예를들어, 20GB 이터널 리턴을 플레이 한다면 16GB렘이 꽂힌 컴퓨터에서도 가능합니다. 이런 현상을 프로그램이 과할당 되었다고 표현합니다.

사실 크게 와닿는 용어는 아니지만, 자료를 찾아보면 위와 같은 내용이 나와서 언급은 하고 가겠습니다.

Page Out

멀티프로세스가 기본이기 때문에, 렘은 다양한 프로세스들에 의해서 쓰여지게 됩니다. x64환경에서는 pageOut이 그렇게 많이 일어나지 않지만, x86환경은 4GB라는 작은 환경에서 유저영역을 생각하면 2GB 밖에 되지않는 작은 공간에서 여러 프로그램들이 돌아야했습니다.

그런 상황에서 OS는 자신이 FreeList에 갖고있는 Frame이 없고, 게다가 Frame 진짜 공간이 없다면 기존 프로세스들이 물고있는 Frame들을 뺏어야합니다. 이 중에서 자주 쓰지 않는 프로세스들이 대상이 되고, 해당 프로세스들의 Frame을 지금 필요로하는 다른 Page(혹은 프로세스)에게 주는걸 Page Out이라고 합니다. 이 순간, 해당 PTE는 Invalid 됩니다.
(기본적으로 프로세스가 자주 쓰이지 않으면, OS는 FreeList를 확보하기 위해서 PageOut을 시킵니다.)

Page Replacement

Disk에서 Ram으로 불러올 때, Ram을 추가적으로 쓸 수 있지만 Page들끼리 교체를 할 수도 있습니다. 이때 교체하는 행위를 Page Replacement라고 하고, Page Replacement에는 다양한 알고리즘이 적용될 수 있습니다.

이제부터 관리대상은 Page

  1. Page Table,Page Directory는 OS가 관리해야 합니다. OS에서 Page Table과 Page Directory와 같은 구현체 정보가 있어야 합니다.

  2. 프로세스가 실제로 Ram을 필요로 할 때(읽거나 쓰는 등의 행위가 일어날 때), Ram이랑 맵핑을 해주어야 합니다. 그전까지는 OS가 관리상의 정보만 갖고 있어도 되고, 실제로 Ram을 정직하게 매핑해주어도 됩니다.(이건 지연 할당에 관한 내용입니다)

  3. Page에 관한 속성정보가 있으면 좋을 것 같습니다. 페이지의 상태를 OS도 알아야 하니까요.

  4. 프로그램은 기본적으로 연속적인 공간을 쓸 확률이 높습니다. 따라서, 연속적인 공간을 보장해주기 위해서, 큰 페이지를 기본 단위로 쓰면 더 좋을 수 있습니다.

독립적인 프로세스들의 공간

가상 메모리를 쓰면서 프로그램마다 각자의 메모리 공간을 갖게 됨으로써 각 프로세스가 독립적으로 존재할 수 있게 되었습니다. 이건 프로그램들을 여러개를 동시에 실행시킬 수 있는 가능성이 제공되었습니다.(아직 방법은 몰?루)

  • 이건 추후에 스케줄링과 Thread, Process에서 Context switching에 관한 내용에서 다루겠습니다.

Page Fault에 관한 상황을 정의에 대해서(⭐⭐⭐)

우리 멍청이 CPU는 이제부터 프로그램이 실행될 때, 실제로 메모리에 있는지 없는지 모르게 됩니다. 메모리에 있다고 생각하고 처리를 계속할겁니다. (Disk에 PageOut을 시킨건 OS니까요.)
따라서,어떤 명령어를 실행할 때마다 Page Fault를 마구마구마구마구마구 발생시킬 겁니다.

이러한 상황을 정상적인 상황인지 비정상적인 상황인지 OS가 판단하여, 정상적이라면 회복할 수 있도록 도와준 뒤, 해당 명령어를 재실행시켜야합니다. 비정상적인 상황이라면 진짜 프로세스를 종료를 해야합니다.

CPU의 Exception의 종류

사실, 아래의 용어들은 편하게 말할 때, 혼용되기도 하기 때문에 정의를 엄밀하게 해야되나 싶을 수도 있습니다. 게다가, CPU 제조사에서도 어떤 플랫폼(칩셋)을 사용하느냐에 따라서, 용어를 재정의해서 자신들만의 예외적인 상황으로 분류(?)하기도 해서.. 혼용이 많이 됩니다.
(여기서 설명하는 용어는 IA-32 Intel® Architecture Software Developer’s Manual, 5 -5 Exception Classification에 근거합니다.)

Trap

  • Intentional exception
    • 의도적인 예외상황
    • Trap 처리가 끝난다면, 다음 명령어를 이어서 실행합니다.(Resume)
  • 대표적으로 SystemCall과 Debugging이 Trap입니다.

Fault

  • Unintentional but possibly recoverable exception
    • 의도적이지 않고, 아마 회복할 수 있는 예외
    • 현재 실행 중인 명령어를 끝까지 실행하지 못하고 Exception Handler로 넘어간 상황.
    • 다시 돌아와서 명령어를 똑같이 실행한다.(실행하지 못하는 명령어는 제외)
    • TLB Miss, Page Fault, Protection violation, privilege violation 등이 있습니다.

Abort

  • Unintentional and unRecoverable fatal errors
  • 의도하지 않았고, 회복 불가능한 치명적인 에러들

Interrupt
참고로, Interrupt와 Exception은 다른개념입니다. Exception은 어떤 처리를 하다가 CPU 내부에서 발생하는 예외적인 상황이고, Interrupt는 CPU 외부요인으로 인해 발생하게 됩니다. 타이머 인터럽트, 키보드/화면/네트워크/DISK와 같은 외부장치 처리를 위한 Interrupt가 있습니다. Interrupt는 언제,어디서든 발생이 가능합니다.

CPU가 페이지 Fault를 발생시켰을 때, OS가 Excepection Handler를 발생시켜서 도와주게 된다는 이야기를 하고 싶었습니다. 그래서, 이름은 Page fault인데.. OS 입장에서는 Trap(?)이라고 표현해도 무방하긴 할 것 같습니다.(어쨋든 Kernel코드에서 page fault상황을 다루어야하니까.)
사실 중요한건 아닌데, 제가 혼동이 자주되서 적어놨습니다.

Page Fault가 나는 상황 4가지

  1. 현재 메모리에 없지만, 페이지 파일 혹은 mapped file에 있는 Page에 접근하는 경우
  2. Accessing a page that is on the standby or modified list
  3. Accessing a demand-zero page
  4. Writing to a copy-on-write page

위는 Windows Internals에 나와있는 내용입니다. 번역서에서는 Transition /UnKnown등으로 나와있어서 헷갈리지만, StackOverflow에서 위의 4가지로 깔끔하게 정리되어 있어서 복사해왔습니다. 모든 상황이 다 머리속으로 시뮬레이션 되어야 하지만, 집중해서 볼건 1번 케이스와 ,3번 케이스 입니다.

페이지 파일 혹은 mapped file에 있는 Page에 접근하는 경우

Page Out된 메모리를 접근하는 경우를 가리킵니다. Disk에 있던 Page를 다시 비어있는 렘 공간에 Page를 올리는 작업을 진행하게 됩니다. Present Bit이 0인 경우, Page Out시 남은 PTE공간은 OS가 자유롭게 사용하기 때문에, 해당 페이지에 대한 정보를 PTE에 적은 뒤, 페이지 파일에 보관하게 됩니다. DMA가 Disk와 통신을 하기 때문에, 꽤 느린 작업입니다.

Accessing a demand-zero page의 경우

메모리 할당하는 과정을 이야기하는데 VAD를 보고 VAD에서 Commit된 영역이라면,이제서야 메모리중에 빈공간을 새롭게 할당해주는 과정을 이야기합니다. 이 과정에서 당연하게 CPU입장에서는 없는 메모리를 건드렸기 때문에 Page Fault가 발생한다. 만약, Commit되지 않은 영역이라면, 엑세스 거부 에러를 발생시킵니다.

VAD (virtual Address Descriptor)
Windows에서 관리를 위해 가상주소공간에 대한 정보를 트리형태로 관리하고 있는 구조체입니다. VAD에서 가상주소공간이 Commit인지, 예약인지,Guard 등의 속성정보를 가지고 있습니다.
VAD를 통해서 Heap에서 할당받을 때, 지연할당을 구현합니다. (Page fault시, Commit이라면 할당, reserve거나 free영역이라면 액세스 위반)

PFN database
Frame이 어디가 비어져있는지 OS는 알아야한다. 그걸위한 관리정보이다.

standby or modified list에 있는 페이지의 경우

StandByList는 page가 Process의 workingSet에서 제거 되었지만, 여전히 각각의 WorkingSet과 연결되어있는 Page들을 이야기합니다. 이는 Cache역할을 위해서, page-fault가 발생했을 때, standbyList에 존재하는 page는 OS가 page file을 뒤지거나, Ram을 살펴볼 필요없이 바로 자신이 갖고있는걸 연결해줍니다. Soft Pagefault라고도 한다.

Working Set
프레임과 대응되어 프로세스가 활용하고 있는 실제 물리적 공간
Commit Page
커밋된 페이지 , Virtual Memory에서 프로세스가 사용하는 총 용량.

페이징 fault과정 중에는 위의 설명을 보면 알 수 있겠지만, IO과정이 빈번하게 일어납니다.

In-Paging I/O
Paging fault 과정 중 , I/O동작이 일어날 수 밖에 없습니다. Ram을 읽는 과정, 페이지 파일을 읽는 과정 등이 있을 수 있습니다. 이러한 행위를 In-Paging I/O라고 합니다.

4번째 경우는 DLL을 할 때 다루도록 하겠습니다. 그리고, 메모리 속성에 관한 내용은 다음 글인 Windows 메모리 API의 글에서 다루도록 하겠습니다. 감사합니다.

0개의 댓글