가상 메모리 기술
2차 저장장치를 메인 메모리를 "캐시"로 사용하는 기술로 여러 프로그램을 동시에 실행하여 메모리를 효과적으로 공유하고 메인 메모리 장치의 크기 한계를 극복하여 더 큰 프로그램을 실행할 수 있게 해 준다.
프로그램 데이터 보호
- 프로그램마다 사용하는 주소 공간을 따로 두고 이를 보호한다 : 각 프로그램들이 자신만의 주소 공간을 가지고 가상 메모리 기술이 가상 주소 공간에 대해 실제 주소로 변환해준다.
페이징 기법
프로세서는 가상 주소를 만들어서 사용하는 반면, 메모리는 실제 주소를 사용하여 데이터에 접근한다. 가상 메모리와 주 메모리는 일정한 크기의 조각으로 나뉜 페이지로 구성되어 있으며 각 가상 페이지 하나는 주 메모리 페이지 하나에 사상된다.
RISC-V에서 주소는 64비트를 가지지만, 실제로 사용되는 비트는 48비트만 사용된다. 페이지 크기가 4 KiB이라면 12 비트를 이용하여 정확한 주소를 찾아야 하므로 page offset은 12 비트를 가진다.
따라서 나머지 가상 주소의 36 비트는 페이지 번호를 실제 주소의 28비트는 물리적 페이지 번호를 나타내는 데 사용한다.
주로 전체 가상 주소의 크기가 실제 주소 크기보다 크므로 어떤 페이지는 주 메모리에 있지 않고 2차 기억장치에 저장되어 있다. 이런 상황을 페이지 부재(Page Fault)라고 한다.
페이지 배치와 다시 찾기
가상 주소를 실제 주소로 변환하기 위해 페이지 테이블이라는 자료 구조를 이용한다. 페이지 테이블은 가상 주소의 페이지 번호로 인덱스 되며, 대응되는 실제 페이지가 기록되어 있다. 이러한 페이지 테이블 자료구조가 저장되어 있는 주소를 담고 있는 레지스터가 있는데, 페이지 테이블 레지스터라고 한다.
페이지 테이블을 구성하는 엔트리에는 Valid 유효 비트가 포함되어 있어 해당 페이지가 실제 메모리에 있는지를 가리킨다. 페이지 주소 변위 Offset 은 가상 주소와 물리 주소가 같이 공유하고 나머지 가상 페이지 번호에 대응되는 물리 페이지 번호가 기록되어 있다. 위 그림에서 페이지 크기는 $2^{12}$ = 4 KiB, 가상 주소의 크기는 48비트로 표현되므로 $2^{48} B = 256 TiB$ 공간을 가질 수 있다. 여기서 페이지 번호가 36비트를 사용하기 때문에 페이지 테이블 엔트리의 개수는 $2^{36}$개가 된다.
페이지 부재
참조하려고 하는 가상 페이지 번호가 유효 비트가 0이라면 페이지 부재가 발생하여 운영체제가 해당 페이지를 디스크에서 가져와 주 메모리에 배치시켜야 한다. 디스크에서 해당 주소의 데이터를 가져오기 위해 가상 메모리 시스템은 디스크 상 주소 역시 알아야 한다. 디스크 상 주소는 페이지 테이블에 기록될 수 도 있고 다른 자료구조에 저장되어 인덱스 될 수 도 있다.
처음 프로그램을 실행하면 어떤 페이지가 메모리에 올라와야 하는지 알 수 없으므로 모든 페이지를 위한 공간을 플래시나 디스크상에 저장한다. 이런 공간을 스왑 스페이스(swap space)라고 부른다.
운영체제는 또한 디스크에서 가져온 페이지를 주 메모리에 배치시키기 전에 주 메모리가 꽉 찼다면 적재되어 있는 페이지 중 하나를 디스크로 내보내야 한다. 이러한 페이지를 희생자 페이지(victim) 혹으 대상 페이지이라고도 하며 보통 LRU 알고리즘에 의해 가장 오래된 페이지를 디스크로 내보낸다. (스왑 아웃)
LRU 알고리즘
가장 오래된 페이지를 선별하기 위해 페이지 테이블 엔트리에 참조 비트(사용/접근) 비트를 추가하고 참조될 때마다 1로 설정한다. 또한 주기적으로 0으로 초기화하며 페이지 교체가 필요할 시 이 비트를 참고하여 희생자 페이지를 결정한다.
큰 가상 주소를 위한 가상 메모리
큰 가상 주소를 사용하면 페이즈 테이블 엔트리 개수가 증가하고 페이지 테이블 전체 사이즈가 증가하여 이를 주 메모리에서 관리하는 것도 부담이 든다.
RISC-V에서는 48 비트 가상 주소를 위하여 4단계로 나누어 물리 주소로 변환한다. 상위 8비트 식을 이용하여 다음 레벨의 페이지 테이블을 인덱스 하게 되고 마지막 레벨에서 4 KiB 페이지에 해당하는 물리 페이지 번호를 찾을 수 있게 된다.
갱신 비트 dirty bit
가상 주소에서 데이터 쓰기는 주 메모리뿐만 아니라 디스크의 접근이 필요하다는 점을 시시한다. 디스크의 쓰기는 주 메모리보다 매우 느리므로 데이터 쓰기가 발생할 때마다 디스크를 접근한다면 성능이 매우 안 좋아진다. 따라서 페이지 엔트리에 갱신 비트를 추가하여 해당 페이지가 최근에 쓰기가 이루어졌는지 기록하고 디스크로 내보내질 때 쓰기가 이루어지도록 한다.
TLB (Translation-lookaside buffer)
페이지 테이블은 주 메모리에 저장되기 때문에 프로그램에 의한 모든 메모리 접근 시간은 최소 2배가 된다. 페이지 테이블을 접근하기 전에 메모리 접근 특성, 지역성과 공간성을 사용하는 캐시 하드웨어를 두어 실제 주소를 빠르게 알 수 있게 한다.
TLB라는 변환 참조용 버퍼는 가상 주소의 일부분 비트를 태그로 두고 가상 주소 쿼리에 대해 캐싱될 수 있도록 한다. 페이지 테이블 엔트리와 동일하게 유효/참조/갱신 비트가 존재하며 유효하지 않다면 TLB miss로 페이지 테이블을 참조하도록 폴 백한다. 이때 페이지 테이블 엔트리에서도 유효 비트가 0이라면 페이지 부재가 발생한다. TLB 실패로 페이지 테이블에서 페이지를 가져온다면 내보내는 페이지는 참조/갱신 비트를 사용하여 선정한다.
Intrinsity FastMATH 하드웨어의 TLB가 동작하는 과정을 묘사되어 있다. TLB 엔트리는 16~512개로 페이지 테이블 엔트리 개수보다 적기 때문에 모든 가상 주소를 표현할 수 없다. 따라서 엔트리를 모두 한꺼번에 비교하는 완전 연관 (Fully-associated) 방식을 사용한다. TLB 히트라면 해당 물리 페이지 번호와 오프셋으로 주 메모리를 참조하게 되는데, 그전에 캐시에 데이터가 있을 수 있으므로 캐시 태그와 인덱스를 추출하여 캐시에 있는지 확인한다. 18 + 8 비트가 캐시를 조회하는 데 사용하고 4 비트는 해당 연관 셋에서 블록 오프셋을 찾기 위해 사용한다.
TLB 실패와 페이지 부재 처리
TLB 실패는 다음 두가지 중 한 경우로 인해 발생한다.
1. 페이지가 메모리에 있는 경우 실패된 TLB 엔트리만 만들면 된다.
2. 페이지가 메모리에 없는 경우 페이지 부재를 처리하기 위해 제어를 운영체제로 넘겨야 한다.
TLB 실패 혹은 페이지 부재는 실행 중인 프로세스를 인터럽트하여, 운영체제로 제어를 넘기고 중단된 프로세스를 나중에 실행하게 된다.
운영체제가 페이지 부재를 일으킨 가상 주소를 알게 되면, 다음 3단계를 수행한다.
1. 가상 주소를 사용해서 페이지 테이블 엔트리를 찾고 디스크 내에서 참조된 페이지의 위치를 찾는다.
2. 교체 시킬 실제 페이지를 설정한다. 만약 선정된 페이지의 갱신 비트가 1 이면 새로운 페이지를 가져오기 전에 이 페이지를 디스크에 써야한다.
3. 참조된 페이지를 선정된 실제 페이지로 가져오기 위해 디스크 읽기를 시작한다.
마지막 단계는 수백만 사이클이 걸리므로 운영체제는 디스크 접근이 끝날 때까지 다른 프로세스를 선택하여 실행한다.
1. 디스크에서 페이지 읽기가 끝나면 페이지 부재를 일으킨 프로세스의 상태를 복구하고 예외에서 복귀하는 명령어를 수행한다.
2. 이 명령어는 프로그램 카운터를 복구할 뿐만 아니라, 프로세서를 커널 모드에서 사용자 모드로 바꾼다.
3. 사용자 프로세스는 부재를 일으킨 명령어를 재실행하고 요구된 페이지를 접근해서 계속 실행한다.
페이지 부재를 줄이기 위한 방법
1. 공간적 지역성을 이용하기 위해 페이지 크기를 크게 만든다. (TLB와 테이블 엔트리에 더 많은 물리 페이지가 들어갈 수 있다.)
2. 페이지 테이블로 구현된 가상 주소와 실제 주소의 사상을 완전 연관 방식으로 구현하여 가상 페이지를 메인 메모리 내 어느 곳에나 적재 시킬 수 있게한다.
3. 교체될 페이지를 선정하기 위해 LRU와 같은 효과적인 알고리즘을 사용한다.
스래싱 (thrashing)
메모리와 디스크 사이에서 페이지 부재가 빈번해져서 시스템 성능이 저하되는 것을 의미한다.
-> 동시에 사용하는 페이지 수(워킹 셋)를 조절한다.
참고 : Computer Organization and Design RISC-V edition
'Computer Science 기본 지식 > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] 함수 호출 규약 (0) | 2021.05.01 |
---|---|
[컴퓨터 구조] 스택 포인터와 프레임 포인터 (0) | 2021.05.01 |
[컴퓨터 구조] 11. 캐시 성능 측정 및 향상 (0) | 2021.04.26 |
[컴퓨터 구조] 10. 메모리 기술 (0) | 2021.04.25 |
[컴퓨터 구조] 9. 명령어 수준 병렬성 (0) | 2021.04.19 |