페이징
페이징은 프로세스의 물리적 주소 공간이 연속적일 필요가 없는 메모리 관리 기법입니다. 페이징은 외부 단편화(external fragmentation)와 압축(compaction) 문제를 해결합니다. 페이징은 운영체제와 컴퓨터 하드웨어의 협력으로 구현됩니다.
기본 방법 (Basic Method)
페이징을 구현하는 기본적인 방법은 물리 메모리를 **프레임(frames)**이라 불리는 고정된 크기의 블록으로 나누고, 논리적 메모리를 **페이지(pages)**라 불리는 동일한 크기의 블록으로 나누는 것입니다. 프로세스가 실행될 때, 해당 페이지들은 보조 저장 장치(secondary storage)에서 사용 가능한 메모리 프레임으로 로드됩니다.
CPU가 생성하는 모든 주소는 **페이지 번호(p)**와 **페이지 오프셋(d)**의 두 부분으로 나뉩니다. 페이지 번호는 프로세스별 **페이지 테이블(page table)**의 인덱스로 사용되며, 페이지 테이블은 물리 메모리의 각 프레임에 대한 시작 주소를 포함합니다. 페이지 오프셋은 해당 프레임 내의 특정 위치입니다.
논리적 주소 변환 과정:
CPU는 논리적 주소에서 페이지 번호(p)를 추출합니다.
페이지 번호(p)를 페이지 테이블의 인덱스로 사용하여, 해당하는 프레임 번호(f)를 얻습니다.
논리적 주소의 페이지 번호(p)를 프레임 번호(f)로 교체합니다.
오프셋(d)은 그대로 유지되어, 프레임 번호와 오프셋이 결합된 최종적인 물리적 주소가 생성됩니다.
페이지 크기는 하드웨어에 의해 결정되며, 보통 2의 거듭제곱(4KB에서 1GB)입니다. 페이지 크기가 2^n 바이트이고 논리적 주소 공간의 크기가 2^m이라면, 논리적 주소의 상위 (m-n) 비트는 페이지 번호를, 하위 n 비트는 페이지 오프셋을 나타냅니다.
페이징은 외부 단편화 문제를 해결하지만, 페이지의 마지막 프레임이 완전히 채워지지 않을 경우 **내부 단편화(internal fragmentation)**가 발생할 수 있습니다. 운영체제는 시스템 전체의 **프레임 테이블(frame table)**을 유지하며, 각 프레임이 할당되었는지, 사용 가능한지, 그리고 할당되었다면 어느 프로세스의 어느 페이지에 할당되었는지 추적합니다.
하드웨어 지원 (Hardware Support)
페이지 테이블이 작을 경우, 고속 하드웨어 레지스터로 구현할 수 있어 주소 변환이 매우 효율적입니다. 하지만 페이지 테이블이 클 경우, 메인 메모리에 저장하고 **페이지 테이블 베이스 레지스터(PTBR)**가 페이지 테이블을 가리키게 합니다.
메모리 접근 속도 저하를 해결하기 위해, **TLB(translation look-aside buffer)**라는 작고 빠른 하드웨어 캐시가 사용됩니다. TLB는 자주 사용되는 페이지 테이블 항목들을 저장하는 연관 메모리입니다.
TLB hit: CPU가 생성한 페이지 번호가 TLB에 있으면, 프레임 번호를 즉시 얻어 메모리에 접근합니다.
TLB miss: 페이지 번호가 TLB에 없으면, 메인 메모리에 있는 페이지 테이블에 접근하여 프레임 번호를 얻습니다. 이후 TLB에 이 항목을 추가합니다.
TLB의 효율성은 **적중률(hit ratio)**에 따라 달라집니다. TLB는 **주소 공간 식별자(ASID)**를 사용하여 문맥 교환 시 TLB를 전체적으로 지우지 않고 여러 프로세스의 항목을 동시에 포함할 수 있습니다.
보호 (Protection)
페이징 환경에서 메모리 보호는 페이지 테이블의 각 항목에 있는 **보호 비트(protection bits)**로 구현됩니다. 이 비트는 페이지에 대한 읽기(read), 쓰기(write), 실행(execute) 등의 접근 권한을 정의합니다. 하드웨어는 모든 메모리 참조 시 이 비트를 확인하여 권한 위반 시 트랩(trap)을 발생시킵니다.
페이지 테이블 항목에는 페이지가 프로세스의 논리적 주소 공간에 속하는지 여부를 나타내는 유효-무효(valid-invalid) 비트가 추가로 붙습니다. 이 비트가 invalid
로 설정된 페이지에 접근하면 하드웨어 트랩이 발생합니다.
페이지 공유 (Shared Pages)
페이징의 장점 중 하나는 **재진입 가능 코드(reentrant code)**를 공유할 수 있다는 것입니다. 재진입 가능 코드는 실행 중에 변경되지 않는 코드로, 여러 프로세스가 동시에 실행할 수 있습니다. 시스템 라이브러리(libc)와 같이 일반적으로 사용되는 코드는 물리 메모리에 한 번만 적재하고, 여러 프로세스의 페이지 테이블이 동일한 물리적 페이지를 가리키도록 매핑하여 메모리 사용을 절약할 수 있습니다. 동적 연결 라이브러리(DLLs)도 이 방식을 사용하여 구현됩니다.
Last updated