Windows OS - 가상 메모리 주소, 물리 메모리 주소
가상주소와 물리주소
가상주소는 말 그대로 가상의 주소이다.
물리주소는 실제 RAM에 위치한 데이터의 주소이다.
이건 일반적으로 구현하는 App에서는 알 수 없다.
App에서 사용하는 모든 주소는 가상주소 이다.
현재 사용중인 PC의 메인메모리(RAM) 용량을 보자.
내 경우엔 16GB 이다.
물리적인 주소는 0x0 ~ 0x 000000FFFFFFFFFF 까지 가능하다.
그런데 실제로 프로그래밍을 해보면
나의 PC에 꽂힌, RAM이 표현할 수 있는 물리적인 영역을 아득히 초과한
번지수가 나오는 경우가 분명히 있다.
App에서 사용하는 주소값은 가상 메모리주소이기 때문이다.
메모리 사용시에도 역시 OS와 하드웨어의 통제를 받고 있다.
이 가상 메모리 주소 와 실제 물리적인 주소와의 변환을 MMU가 수행한다.
가상 메모리 주소에 대한 관리 및 물리주소와의 매핑을 수행함으로서,
App의 안전한 메모리 사용을 유도한다.
또한 x64 윈도우즈에서의 주소최대값은 2^64 -1 번지 까지이다.
이는 매우 큰 값이다.
이렇게 큰 범위를 가지는 가상 주소값을 사용할 수 있기 때문에,
메모리를 플렉서블하게 사용할 수 있는 이점또한 가져간다.
거듭 강조하지만 가상주소는 실제 물리적인 어드레스가 아니다.
또한 이러한 가상주소 공간은 프로세스마다 개별적이다.
그리고 이것을 실제로 물리메모리에 매핑해주고 매니지먼트하는 것은
MMU라는 하드웨어가 수행한다.
예를 들어 서로 다른 프로세스 A 와 프로세스 B 를 구동하는 코드에서
같은 주소값 12345번지에 접근한다고 하자.
12345는 가상주소값이다.
이 가상주소값에 대한 접근을 OS가 관리한다.
프로세스 A의 ~번지 부터 ~번지는 물리메모리 ~번지 부터 ~번지.
B의 ~번지 부터 ~ 번지는 물리메모리 ~번지 부터 ~번지.
같은 식으로.
App에서의 주소값이 가리키는 물리메모리의 주소는 다를 수 밖에 없다.
프로세스 내부의 가상주소값을 물리메모리 영역에 매핑하는 과정은 전적으로 OS의 몫이다.
생각해보면 매우 당연한 이야기이다.두 프로세스가 같은 물리매모리 공간을 공유한다면,
메모리에 읽고 쓰는 동작 자체가 제대로 작동하지 않을 것이다.
프로세스에 속하는 각각의 스레드는 같은 가상주소 매핑 규칙을 공유한다.
프로세스 A에 속하는 스레드 T1 과 T2 이 있다고 하면
두개의 스레드에서의 가상주소값 12345는 같은 물리메모리 영역이다.
가상주소 어드레싱 규칙(가상주소 공간의 용도별 분할.)
각 프로세스의 가상주소 공간 내에서도
각각의 주소 구간은 용도에 맞게 분할되어있다.
이를 가리켜 파티션 이라고 한다.
상술했듯 프로세스마다 물리메모리에 매핑된 가상주소가 다르기는 하지만,
어찌되었던 가상주소를 사용할때에도 모든 프로세스가 따라야하는 규칙이 있다는 것이다.
이를 가리켜 가상주소 파티션이라고 한다.
이 분할규칙은 네가지로 이루어진다.
가상주소의 최상위 0번지 부터 차례대로
NULL 포인터 할당 파티션, 유저모드 파티션, 64KB 접근 금지 영역, 커널모드 파티션이다.
NULL 포인터 할당 파티션
App에서 접근 금지 위반을 일으키기 위해 만들어져 있다.
상술했듯 모든 프로세스는 파티션 규칙을 따른다.
때문에 NULL 포인터를 역참조하면 그 어떤 프로세스도 살아남지 못한다.
이제 NULL 포인터 역참조를 하면 Access Violation을 일으키는
이유가 이해가 갈 것이다.
애초부터 그러라고 만들어진 지옥의 문에 접근했던 것이다.
유저모드 파티션
x86 아키텍처 CPU의 경우 0x00010000 ~ 0x7FFEFFFF (2GB) 이다.
x64 아키텍처의 CPU의 경우 0x00000000 00010000 ~ 0x000007FFF FFEFFFF (8192GB) 이다.
IA-64 아키텍처의 CPU의 경우 0x00000000 00010000 ~ 0x000006FB FFFEFFFF (7152GB) 이다.
어짜피 가상주소 공간이기는 하지만, OS는 아주 낮은 확률을 제외하고는 이 영역조차
프로세스마다 겹치지 않게 한다.
x64에서는 주소값의 범위가 워낙 커서 이런식으로 구별이 되게끔 한정을해도 별 관계가 없다.
그러니 앞서 예시로 제시했던 12345번지의 예시도 사실은
매니지먼트 유닛을 통한 간접접근의 개념을 설명하기 위한 예시이고, 경험해보기 힘든 예시이다.
프로세스의 코드영역, 데이터영역, App에서 사용하는 스택주소,
동적할당 받은 모든 포인터 변수의 값들은 모두 여기에 속한다.
어떠한 경우 에서도 넘어갈 수 없다.
프로그래밍시에는 유저모드 파티션의 이러한 규칙을 이용해
포인터 변수의 최상위 비트에 플래그세팅을 하기도 한다.
물론 포인터변수를 이용해 메모리 접근시에는 플래그 세팅을 해제하고 읽어야겠다.
그렇지 않으면 여지없이 Access Violation을 보게 될 것이다.
x86 만을 타겟으로 했던 많은 프로그램들이 x64환경에서 잘 동작하지 않을 수 있는 이유중 하나가
여기에 있다.
App내에서의 모든 포인터값이 0x00010000 ~ 0x7FFEFFFF 로, 최상위비트 세팅이 되지 않을 것이라고
예상하고 코드를 작성했기 때문이다.
이 프로그램을 x64환경에서 돌리면 어떻게 될까.
단지 큰 주소값인것 뿐이고 2GB이상의 메모리인 것 뿐인데,
실제로 설정되지 않은 플래그를 설정된것으로 인식할뿐아니라
메모리 접근시 포인터변수의 비트를 해제해 버리는 것이다.
그래서 이런 x86 시절에 작성된 어플리케이션에 대한 호환성때문에,
기본적으로 VS에는 /LARGEADDRESSAWARE 링크옵션이 존재하고,
이 옵션이 해제되어있다.
/LARGEADDRESSAWARE 링크옵션이 설정되면, 2GB이상의 주소공간을 사용한다.
그렇지 않으면 상술한 이유로 4byte내에서 사용이 가능한 포인터값만을 사용할 수 밖에
없도록 되어있다.
애초부터 x64를 염두하고 프로그램을 짰다면 /LARGEADDRESSAWARE 링크옵션을 설정하도록 한다.
그래야 2GB이상의 메모리 영역에 대한 포인터 값을 제대로 사용할 수 있다.
64KB 접근 금지 구역
이 구간은 유저모드 파티션과 커널 모드 파티션 사이에 위치한 영역이다.
유저모드의 마지막 주소로부터 x86, x64, IA-64 모두 64KB로 이루어진
이 공간은, 유저모드 파티션과 커널 모드 파티션 사이의 구분을 위해서
존재하는 공간이다.
당연히 접근하면 Access Violation이다.
커널모드 파티션
64KB접근 금지 구역부터 하위 마지막 주소까지 이 영역이 존재하게 된다.
운영체제를 구성하는 코드들, 그들이 사용하는 메모리가 위치한다.
운영체제를 구성하는 코드와 커널을 위한 메모리 영역이다.
그러니 당연히 접근시 Access Violation이다.
여기에 접근하는 일은 당연히 없어야 한다.
개념적으로 분할을 위한 영역으로 존재하므로, 응용프로그램의
작성시 이 공간에 대해 신경을 쓸 일은 존재 하지 않을것이다.
'Windows OS' 카테고리의 다른 글
Windows OS - Virtual Memory 사용 VirtualAlloc, VirtualFree (0) | 2022.02.28 |
---|---|
Windows OS - Windows 제공 Reader/Writer lock SRWLock (0) | 2022.02.20 |
Windows OS - CriticalSection Spinlock으로 사용하기 (0) | 2022.02.20 |
Windows OS - 스레드(Thread) 2편 (0) | 2022.02.01 |
Windows OS - 스레드(Thread) 1편 (0) | 2022.01.12 |
댓글
이 글 공유하기
다른 글
-
Windows OS - Virtual Memory 사용 VirtualAlloc, VirtualFree
Windows OS - Virtual Memory 사용 VirtualAlloc, VirtualFree
2022.02.28 -
Windows OS - Windows 제공 Reader/Writer lock SRWLock
Windows OS - Windows 제공 Reader/Writer lock SRWLock
2022.02.20 -
Windows OS - CriticalSection Spinlock으로 사용하기
Windows OS - CriticalSection Spinlock으로 사용하기
2022.02.20 -
Windows OS - 스레드(Thread) 2편
Windows OS - 스레드(Thread) 2편
2022.02.01