이번에 공부해 볼 내용은 페이징입니다. 바로 앞의 글에서는 하나의 프로세스가 하나의 파티션을 할당받는 연속적인 메모리 할당에 대해서 공부해보았습니다. 하지만 메모리는 비연속적인 물리적 주소 공간을 할당해 줄 수도 있습니다. 그리고 이런 비연속적인 할당을 통해 external fragmentation을 예방할 수 있습니다.
1. 페이징 (Paging)
페이징의 기본적인 방법은 다음과 같습니다. 물리 메모리(physical memory)는 프레임(frame)이라고 불리는 일정한 크기의 고정된 사이즈의 블록들로 나누어집니다. 그리고 논리 메모리(logical memory)는 페이지(page)라고 불리는 프레임과 같은 사이즈의 블록으로 이루어집니다. 그리고 논리 주소는 페이지 번호(page number)와 페이지 변위(page offset)로 이루어지며, 여기서 페이지 번호는 페이지 테이블에서의 인덱스로 사용이 되며, 페이지 변위는 페이지 내에서 데이터를 찾을 때 사용됩니다. 그리고 물리 주소는 프레임 번호(frame number)와 프레임 변위(frame offset)로 이루어지게 됩니다. 아래의 그림들을 보면서 어떤 식으로 흐름이 흘러가는지 이해하면 될 것 같습니다.
한 프로세스가 실행되기 위해 도착하면, 그 프로세스가 몇 개의 페이지를 사용해야 하는지를 조사합니다. 하나의 페이지는 하나의 프레임을 필요로 하기 때문에 메모리에 가용한 프레임의 수를 고려해주어야 합니다. 프레임의 개수에 여유가 있다면, 프로세스에게 필요한 수의 프레임들을 할당해 줍니다. 그리고 프로세스가 프레임을 사용할 때마다 페이지 테이블에 프레임의 번호를 기록해줍니다.
이런 방식을 사용하면 각각의 프로세스들은 물리 메모리에서 굳이 연속된 파티션을 할당 받을 필요가 없습니다. 물리 메모리의 어떤 프레임이든 비어있다면 사용할 수 있기 때문에 external fragmentation은 사라지게 되고, 메모리 사용의 효율을 크게 늘릴 수 있습니다.
페이지의 사이즈를 얼마나 크게 해야하는 지도 고민거리가 될 수 있습니다. 페이지 사이즈가 너무 작아지게 되면 페이지 테이블이 커지게 되어 공간이 낭비될 수 있고, 또 디스크의 입장에서 볼 때 페이지의 크기가 클수록 유리하기 때문에 일반적으로는 메인 메모리가 커짐에 따라 페이지의 크기도 덩달아 커져왔습니다. 현재는 Windows 10은 4KB와 2MB의 페이지를, Linux는 기본적으로 4KB의 페이지를 지원합니다.
2. 하드웨어 지원
운영체제는 이러한 페이지 테이블을 사용하기 위해 여러 방법들을 지원합니다
가장 쉬운 방법은 페이지 테이블 정보를 저장하고 있는 레지스터 파일을 CPU 내에 저장해 두는 것입니다. CPU 내에 페이지 테이블이 있기 때문에 굉장히 빠르게 처리를 할 수 있다는 장점이 있지만 큰 사이즈의 페이지 테이블은 사용할 수 없다는 단점이 있습니다.
그래서 그 다음 방법은 페이지 테이블을 메모리로 빼고 CPU에는 페이지 테이블의 주소 값을 저장하는 레지스터만 남기는 것입니다. 이 레지스터를 PTBR(Page-Table base register)이라고 합니다. 페이지 테이블은 프로세스마다 존재하기 때문에 앞에서의 방법은 Context switch가 일어나면 CPU 내의 페이지 테이블을 완전히 다 새로운 테이블로 바꿔줘야 하지만, 이 방법에서는 PTBR만 바꿔주면 되기 때문에 Context Switch Time을 줄일 수 있습니다.
하지만 이 방법도 문제점이 있습니다. 바로 메모리에 접근하는 횟수가 너무 많아진다는 것입니다. 원래 메모리 접근 자체가 시간이 많이 걸리는 작업입니다. 그런데 이런 방법을 사용하면 하나의 데이터를 읽어오기 위해 2번이나 메모리에 접근해야 합니다. 우선 메모리에 있는 페이지 테이블에 접근해서 프레임 번호를 알아오고, 이것을 다시 CPU로 가져와서 MMU에서 물리 주소를 만든 뒤, 그 주소를 따라 다시 메모리로 가서 원래 원하던 데이터를 가져와야 하는 번거로운 작업을 거쳐야 하는 것입니다.
3. TLB
이를 해결하기 위해 TLB(Translation Look-aside Buffer)라는 것을 사용합니다.
TLB는 특수한 작은 소형 하드웨어 캐시이며, TLB 내의 각 항목은 key(페이지 번호)와 value(프레임 번호)로 구성됩니다. TLB는 페이지 테이블과 유사한 형태를 가지는 것이죠. 우선 데이터를 찾아야 할 일이 생기면 메모리에 있는 페이지 테이블에 가기 전에 TLB를 먼저 살펴봅니다. TLB의 key 중에 내가 원하는 페이지의 번호가 있으면 해당 value를 프레임 번호로써 가져오면 되는 것이죠. 다시 말해 CPU 내에 있는 페이지 테이블의 일부만을 저장하는 임시 저장소라고 볼 수 있습니다.
이 임시 저장소가 효율적으로 작동하려면 최대한 자주 사용하는 페이지들이 TLB에 저장되어 있는 것이 좋겠죠? TLB에 원하는 페이지의 번호가 저장되어 있다면 메모리에는 그 데이터를 찾기 위해 한번만 접근하면 되기 때문입니다. 만일 원하는 페이지가 TLB에 존재한다면 이것을 TLB Hit라고 부르고, 존재하지 않는다면 이것을 TLB Miss라고 부릅니다. TLB가 가득차면 TLB에 어떤 페이지를 남기고 어떤 페이지를 삭제시킬지를 고민해 보아야 하며, 최대한 TLB Hit를 높일 수 있는 알고리즘을 이용해 TLB를 관리하는 것이 중요합니다.
Context Switch가 일어나면 원칙적으로는 TLB의 모든 정보를 지워버려야 합니다. 모든 프로세스는 자기 자신의 페이지 테이블과 논리적 주소 공간을 가지고 있기 때문에, 프로세스 간에 페이지의 번호가 같은 경우도 충분히 있을 수 있습니다. 그래서 CPU 내에 다른 프로세스의 TLB가 남아있다면 다른 프로세스의 메모리 공간에 잘못 접근하는 사태가 벌어질 수 있기 때문입니다. 앞에서도 말했지만, 다른 프로세스의 메모리 공간에 접근하는 것은 금지되어 있습니다.
하지만 역시 Context Switch가 일어날 때 마다 모든 TLB를 지우고 새로운 정보를 입력하는 것은 너무 비효율적이라고 생각할 수 있습니다. 그래서 여기서는 ASID(Address-Space IDentifiers)라는 것을 사용합니다. TLB에 key와 value 이 외에 ASID라는 정보를 추가로 제공하는 것인데 이 ASID는 해당 페이지가 어떤 프로세스의 페이지인지에 대한 정보를 담고 있습니다. 다시 말해, 페이지를 검색할 때 우선 ASID를 살펴보고 지금 CPU에서 실행 중인 프로세스의 페이지들만 고려하여 검색을 하는 것입니다.
우선 여기까지 페이징에 대해서 알아보았습니다. 다음 글에서도 페이징의 남은 부분을 조금 공부해보고자 합니다. 이제 운영체제도 끝을 향해 달려가고 있습니다. 조금만 더 힘내서 공부할 수 있었으면 좋겠습니다. 감사합니다.
'전공공부 > 운영체제 (Operating System)' 카테고리의 다른 글
[운영체제] Virtual Memory (1) - Demand Paging, Page Replacement (1) | 2021.01.04 |
---|---|
[운영체제] Main Memory (3) - Page Protection, Page Table Structure (0) | 2021.01.04 |
[운영체제] Main Memory (1) - Address Binding, Continuous Memory Allocation (0) | 2021.01.03 |
[운영체제] Deadlock (3) - Deadlock Avoidance (0) | 2021.01.02 |
[운영체제] Deadlock (2) - Deadlock Prevention (0) | 2021.01.01 |