Paging

2 minute read

주소 변환 과정의 요약

어셈블러 프로그래밍에서 쓰이는 주소는
실제 RAM 에 올라가는 주소와 다르다.

Segmentation

우리가 쓰는 주소는 segment 상의 주소이다.
이를 운영체제가 segment registor 의 값을 참조하여 이에 따른
Global Descriptor Table 과 Local Descriptor Table 에서 segment 를 찾고
그에 따른 base 와 offset 으로 선형주소로 변환하며,
이를 segmentation 이라고 부른다.

Paging

segmentated 된 주소는 다시 RAM 의 물리 주소로 변환된다.
선형주소를 약 4kb 단위의 페이지 프레임으로 나누고
이를 메모리의 빈 공간에 올린다. 이때, 현재 진행 중인 페이지만 메모리에 올라간다.
이때 그 메모리 상에서의 페이지를 찾기 위한 주소를 저장하는 자료구조를 페이지 테이블이라고 부른다.
그 페이지 테이블로 찾아진 페이지 주소를 RAM 의 최종적인 물리주소로 변환하는 과정을
Paging 이라고 부른다.

Page Table

페이지 프레임은 4KB 이므로, 1024개의 페이지 주소를 저장할 수 있다.
페이지 테이블은 GDT - LDT - Segment 구조와 같이 트리 구조이다.

맨 위의 페이지 테이블은 1024개의 또다른 페이지 테이블의 포인터를 저장한다.
그리고 그 페이지 테이블들은 1024개의 페이지들을 저장하고 있다.

이 구조로 1024 * 1024개의 4Byte 짜리 페이지, 4GB까지 모두 커버가 가능하다.
두 단계로 나눈 이유는 사실 페이지가 4GB까진 안 가기 때문에 일부만 저장하기 위함이다.

페이지는 앞서 말했듯 실행 중인 페이지만 메모리에 올라가며
나머지 페이지들은 디스크에 존재하며 이를 swap space 라고 한다.
이는 큰 프로그램도 돌리기 위함이며, 동시에 많은 프로그램을 실행하기 위함이다.
페이지 테이블의 트리는 GDT - LDT 트리가 그러했듯, 프로그램마다 하나씩 생겨난다.
또한 그 트리의 맨 위의 페이지 테이블의 주소는 CR3 레지스터에 저장된다.
이는 하나만 존재하는데, 1번 프로그램이 실행 중이다가 2번 프로그램이 실행되면 CR3 의 값도 바뀐다.

Page Fault

프로그램의 실행 중 필요한 페이지가 메모리에 존재하지 않으면 page fault 문제가 발생한다.
page fault 는 일종의 sw interrupt 이고 이 문제가 발생하면 일단 그 페이지의 명령어는 중단되며,
그 페이지를 디스크에서 찾아서 메모리로 올려 문제를 해결하고, 다시 재개한다.

사실 이 문제가 일어날 일은 그렇게 많지가 않다.
왜냐하면 메모리의 전범위에 페이지를 다 뿌려놓고 왔다갔다 하면서 프로그램이 실행되기 보다는
메모리 일부에 집중되는 경우가 많이 때문이다.
일례로 반복문을 실행한다면,
그 반복문에 해당하는 페이지만 메모리에서 계속 참조하기 때문에
이 경우에는 page fault 가 일어날 일이 없다.

페이지 테이블의 맨 마지막 한 비트는 PP라고 불리며
이는 그 페이지가 RAM 에 있는지 없는지를 나타낸다.
페이지를 쓰려는데 0이라면 Page Fault 가 난 것이고,
주소가 유효하지 않으면 Segmentation fault 가 난 것이다.

최종 Paging

32비트 짜리 페이지는 10/10/12비트의 세 부분으로 나뉜다.
상위 10비트는 맨 위 페이지 테이블에서의 주소, (10비트이므로 0 ~ 1023사이의 값, 1024개이다.)
그 다음 10비트는 그 다음 페이지 테이블에서의 주소 (역시 마찬가지)
이로부터 얻어진 페이지의 값에 마지막 12비트를 더한 값이 실제 RAM 상의 물리 주소이다.

ELF 형식

리눅스에서는 파일에서 ELF 형식을 쓴다.
그러나 이는 운영체제마다 그 형식이 다르며,
호환이 안 되면 실행이 불가하다.
따라서 다른 운영체제에서 파일을 실행하려면 그 형식을 변환하거나, 새로 컴파일 해야 한다.
게임이나 다른 프로그램이 운영체제마다 다르게 다운받아지는 것도 이때문이다.