명품 운영체제 9장 - 페이징 메모리 관리
페이징 개념
페이징은 프로세스를 페이지 라고 불리는 고정 크기로 나눈다. 물리 메모리 또한 페이지와 같은 크기인 프레임 으로 분할하여, 프로세스의 각 페이지를 물리 메모리의 프레임에 할당한다.
페이징 기법은 프로세스의 주소 공간을 페이지 크기로 자르기 때문에, 페이지의 경계를 고려하지 않고 코드 데이터 힙을 연이어 배치한다. 하지만 실제 운영체제에서는 코드,데이터,힙을 쉽게 관리하기 위해 데이터와 힙 영역이 서로 다른 페이지에서 시작되도록 배치한다.
페이지의 경계를 고려하지 않는다는게 무슨 말일까? 아래에서 예시를 통해 이해해보자.
페이지 크기는 4KB이고, 프로그램의 논리 주소 공간은 10KB라고 하자. 그리고 프로세스는 아래와 같이 구성된다고 해보자.
코드: 5KB
데이터: 3KB
힙: 2KB
이때 프로세스를 페이징 기법을 사용하여 메모리에 적재한다면 아래와 같다.
페이지 0: 처음 4KB(코드 일부 포함)
페이지 1: 다음 4KB(나머지 코드와 데이터 일부 포함)
페이지 2: 마지막 2KB(나머지 데이터와 힙의 일부 포함)
코드, 데이터, 힙이 논리 주소 공간에 순차적으로 배열되어 있더라도, 페이지 크기에 따라 분할되기 때문에 여러 페이지로 분할된다.
프로세스는 논리적으로 코드,데이터,힙 영역으로 정확히 분리 될 수 있지만 페이징 기법을 사용하면 4KB 라는 페이지 단위로 자르기 때문에 코드의 마지막 페이지 안에 데이터 영역이 포함되고, 데이터의 마지막 페이지 안에 힙 영역이 포함 될 수 있다는 것이다.
중요한 점은 이러한 프레임이 물리적 메모리의 어느 곳에나 분산될 수 있으므로, 논리적 순차 배열(코드 → 데이터 → 힙)이 논리 주소 공간에서만 유지되고 물리적 메모리에서는 유지되지 않는다는 것이다.
페이징은 고정된 크기로 프로세스를 분할하기때문에 홀의 크기가 결국 페이지 크기이다. 따라서 세그먼테이션과 달리, 홀의 크기에 대한 정보를 저장하고 있을 필요가 없다. 그래서 페이지 테이블은 페이지의 물리 주소를 저장하는 항목(프레임 번호) 1개만 가지고 있다.
페이지 테이블을 참조하여 프로세스의 페이지가 물리 메모리의 어떤 프레임에 매핑되어있는지 알 수 있다.
페이지 테이블은 프로세스 마다 만들어지는데, 하나의 프로세스를 페이지 단위로 나누었을때 만들어지는 최대 페이지 개수는 1MB, 약 100만개이다. 따라서 프로세스들의 페이지 테이블을 하나로 관리하기에는 용량이 너무 커지기 때문에 프로세스 당 하나씩 만들도록 설계한것이다.
(프로세스의 주소 공간 4GB / 페이지 크기 4KB) = 1MB
페이지 테이블
프로세스에서 스택과 힙 영역은 프로그램 실행 중에 동적으로 할당 받는 부분이기 때문에, 페이지 테이블의 많은 항목들이 비어있다.
또한 커널 코드도 논리 주소로 작성되어있기 때문에 페이지 테이블을 통한 매핑이 필요하다.
또한 페이지 테이블은 메모리에 저장하기 때문에 cpu 내부에 현재 프로세스의 페이지 테이블 시작 주소를 저장하는 레지스터가 필요하다. (Page Table Base Register)
단편화
프로세스 코드와 데이터가 주소 공간에 연속되어있기 때문에 내부 단편화는 마지막 페이지에서만 발생한다.
이렇게 가정하면 프로세스당 발생하는 단편화는 1/2 페이지 크기, 즉 2KB로 매우 작다.
페이징의 주소 체계
페이징의 논리 주소
페이징을 사용하는 시스템에서는 프로세스의 주소에 특별한 의미가 있다.
예를들어 프로세스의 크기가는 30이고, 페이지의 크기가 10이라고 하자. 그럼 프로세스는 3개의 페이지로 구성된다.
하나의 블록의 크기가 10이므로, 블록 안에서 주소를 0~9로 나누어서 생각할 수 있다.
프로세스의 주소에서 앞에 있는 숫자는 페이지의 번호를 나타내고, 뒤에 있는 숫자는 옵셋을 나타낸다.
옵셋이란 페이지 내에서의 상대적 주소를 말한다.
프로세스의 주소는 논리 주소이기 때문에, 실제 메모리의 주소를 알아내고 싶다면 페이지 테이블을 봐야한다.
페이지 테이블은 프로세스의 페이지가 어떤 프레임에 해당하는지 저장하고 있다. 따라서 논리 주소를 물리 주소로 변환하기 위해서는 페이지 번호를 프레임 번호로 바꾸기만 하면 된다. 이후 옵셋을 이용하여 해당 프레임의 위치로 가면 원하는 데이터가 있는 것이다.
이제 실제 메모리를 이용해서 생각해보자. 32비트 주소선을 가지고 있는 CPU와, 페이지의 크기가 4KB라고 한다면,
페이지 내의 각 바이트 주소(옵셋 주소)를 12비트로 나타낼 수 있다. 그럼 32비트 논리 주소에서 하위 12비트는 페이지 내에서 옵셋의 주소를 나타내고, 상위 20비트는 페이지 번호를 나타낸다.
프로세스의 논리주소를 보고 페이지 번호와 옵셋의 위치를 알아내서 위치를 특정할 수 있게 되는 것이다.
논리 주소 0x12345678의 페이지 번호와 몇번째 바이트에 위치한 주소인지 생각해보자.
하위 3자리 678은 페이지 내의 위치를 의미하고, 상위5자리인 12345는 페이지 번호를 의미한다.
즉, 이 주소는 0x12345번 페이지의 0x678번째 바이트에 대한 주소임을 알 수 있다.
논리 주소의 물리 주소 변환
페이지 테이블에는 프로세스의 모든 페이지에 대해 할당된 프레임 번호가 저장되기 때문에, 페이지 번호를 인덱스로 하여 페이지가 할당된 프레임 번호를 얻을 수 있다.
이렇게 얻은 프레임 번호를 페이지 번호와 바꾸고, 옵셋을 그대로 사용하면 논리 주소를 물리 주소로 바꿀 수 있다.
페이지 테이블의 문제점
페이징 기법이 단순하여 구현하기는 쉽지만, 페이지 테이블로 인한 성능 저하와 공간낭비 라는 두가지 문제점이 있다.
첫번째는 페이지 테이블은 MB 단위로 크기 때문에 cpu 내부에 둘 수 없어서 메모리에 저장하고 사용한다.
그래서 cpu가 메모리를 엑세스 할때마다 페이지 테이블 한번, 물리 메모리 한번 으로 총 2번의 물리 메모리를 엑세스 해야한다. 이는 프로세스의 실행속도를 심각하게 저하시킨다.
두번째는 페이지 테이블은 프로세스의 최대 크기에 맞춰 만들어지지만, 실제 프로세스의 크기는 그에 미치지 못하기 때문에 많은 공간낭비가 있다.
그럼 페이지 테이블로 인한 이 두가지 문제점을 해결하는 방법에 대해서 알아보자
2번의 물리 메모리 엑세스
메모리에서 값을 가져오기 위해서는 메모리를 한번만 접근하는 것 같지만, 사실은 두번 접근한다.
메모리는 cpu에 비하여 속도가 매우 느리기 때문에 메모리에 자주 접근 할 수록 프로세스의 실행시간이 길어지게 된다.
메모리 엑세스= 페이지 테이블 항목 읽기 1번 + 데이터 엑세스 1번
TLB를 이용한 2번의 물리 메모리 엑세스 문제 해결
TLB는 MMU 내에 두는 것으로 CPU가 최근에 접근한 페이지 번호와 페이지가 적재된 프레임 쌍을 저장하는 캐시 메모리이다.
따라서 TLB는 [ 페이지 번호(p), 프레임 번호(f)] 의 쌍을 항목으로 가지고 있다.
TLB는 논리 주소를 물리 주소로 변환할때 사용되므로 주소 변환용 캐시 라고도 불린다.
TLB 캐시에서 페이지 번호가 검색되는 방식은 일반 메모리와 많이 다르다. TLB는 페이지 번호를 순차적으로 검사하지 않고 모든 항목과 동시에 비교하여 단번에 프레임 번호를 찾아낸다.
주소 번지를 사용하지 않고 내용을 직접 비교하여 찾는 메모리 라고 해서 내용-주소화 기억장치 혹은 연관 메모리 라고도 부른다.
TLB의 크기는 cpu에 들어가야 하므로 작아야한다. 따라서 최근에 접근한 소수의 페이지와 프레임의 번호를 저장한다.
TLB를 이용한 메모리 엑세스
현재 대부분의 상용 cpu는 TLB를 내장하고 있다. TLB가 있으면, 반복적으로 메모리를 읽어오지 않아도 되기 때문에 프로그램의 실행속도가 매우 빨라지게 된다.
아래 그림을 통해서 자세히 이해해보자.
cpu로 부터 논리 주소가 발생하게 되면, 페이지 번호를 TLB로 전달한다.
페이지 번호와 TLB 내에 저장된 모든 페이지 번호가 동시에 비교 되고, TLB 히트가 발생하게 되면 바로 주소 변환이 일어나므로 페이지 테이블에 엑세스 하지 않아 물리 메모리를 1번만 엑세스 하게 된다.
만약 TLB 미스가 발생한다면 페이지 2번에 해당하는 프레임 번호를 알아내기 위해 메모리에 접근하여 페이지 테이블에서 프레임 번호를 찾은 뒤, TLB에 페이지 번호와 프레임 번호를 저장해둔다.
- 처음 TLB 미스가 발생하는 한 번의 경우에만 물리 메모리를 2번 엑세스 한다.
- 동일한 페이지를 연속하여 엑세스 하는 동안 TLB 히트가 계속 발생한다.
처음 TLB 미스가 발생한 이후, 미스한 페이지 번호와 프레임 번호가 TLB 캐시에 삽입되므로 그 후 부터는 거의 TLB미스가 발생하지 않는다. 이것이 바로 TLB를 활용하면 CPU의 메모리 엑세스 성능이 좋아지는 이유다.
TLB와 참조 지역성
TLB를 사용한다고 모든 프로그램의 실행 속도가 개선되는 것은 아니다. TLB는 순차 메모리 엑세스 패턴인 경우 매우 효과적이다.
하지만 프로그램이 메모리를 랜덤하게 엑세스 하는 경우, 참조 지역성이 잘 형성되지 않아 TLB 미스가 자주 발생하여 메모리에 엑세스 하는 횟수가 증가하게 된다.
TLB 성능과 도달범위
TLB의 성능은 TLB 히트율 이며 프로그램의 성능과 직결된다. TLB 히트율을 높이기 위해선 많은 항목들을 저장 하고 있으면 되지만, 비용이 매우 비싸진다는 문제점이 있다.
다른 방법으로는 페이지의 크기가 크면 TLB의 히트율이 높아진다. 하지만 페이지의 크기가 커지면 내부 단편화가 증가하여 메모리가 낭비 된다는 문제점이 있다. ( 내부 단편화의 크기 = 페이지의 크기 절반 )
TLB 도달범위란 TLB 캐시의 항목 수와 페이지 크기가 모두 고려된, TLB의 성능을 나타내는 지표로서 간단히 TLB항목 수 X 페이지 크기 로 계산된다.
- TLB 히트율 = cpu의 메모리 엑세스 횟수에 대한 TLB 히트의 횟수의 비율이다.
- TLB 도달 범위 = TLB 항목 수 X 페이지 크기
TLB를 고려한 컨텍스트 스위칭 재정립
페이지 테이블은 프로세스당 하나씩 존재하기 때문에, 동일한 프로세스 내에서 다른 스레드가 실행된다면 TLB에 들어있는 항목들을 교체할 필요가 없지만 다른 프로세스의 스레드를 실행하는 경우에는 페이지 번호에 따른 프레임 번호가 달라지기 때문에 TLB에 들어있는 항목들을 교체해줘야한다.
- CPU의 모든 레지스터를 PCB에 저장
- PCB에 있는 프로세스의 페이지 테이블의 주소를 MMU(CPU)의 Page Table Base Register(PTBR)로 로딩
- TLB 내용 모두 지우기
- 새로 스케줄된 스레드의 TCB에 저장된 레지스터 값들을 CPU에 적재한 후 실행
- TLB 미스가 발생하면서 TLB 캐시가 채워짐
🐸 참고
[운영체제] 9. 페이징 메모리 관리
페이징 메모리 관리 개요 페이징 개념 1) 페이지와 프레임 프로세스의 주소 공간을 0번지부터 동일한 크기의 페이지로 나눔 물리 메모리 역시 0번지부터 페이지 크기로 나누고, 프레임이라고 부
velog.io