[CS 스터디 - 2회차]
발표자 : 민성재
날짜 : 4/17
범위 : 운영체제) 페이징과 세그맨테이션 , 페이지 교체 알고리즘
먼저 페이징은 외부 단편화로 인한 메모리 낭비가 매우 심하고 그때 Compaction(압축)을 사용하면 외부 단편화를 해결할 수는 있지만, 그로 인해 생기는 오버헤드와 비효율적인 성능으로 사용하기 어렵기 때문에 나온 기법이다.
외부 단편화란 그림처럼 공간이 100MB나 있음에도 C작업이 적재되지 못하고 낭비되는 상황을 의미한다. 이런 경우 Compaction 이라는 압축기법을 통해 분산된 자유 메모리 공간을 모아서 큰 블록을 형성하는데 프로세스 주소 공간이 동적으로 재배치되어야 하므로 굉장히 오버헤드가 크고 비효율적이다.
따라서 남은 메모리 공간을 동적으로 재배치하지 않고 프로세스 자체를 잘게 쪼개서 곳곳에 넣는 개념이 바로 페이징이다. 페이징을 통해서 외부 단편화를 해결할 수 있지만 내부 단편화는 여전히 남아있다.
다만, 하나의 프로세스는 연속적으로 시간에 따라 계속 동작을 수행하는데 이를 잘게 쪼개서 여기저기 분산해버리면 프로세스가 정상 작동하는가??
이를 위해서 실제로는 메모리 주소가 연속적이지 않지만 연속적으로 CPU에게 보이게끔 하는 기술이 필요하다.
먼저 프로세스 P1 은 5개의 페이지로 나뉜다. 그리고 메모리의 조각은 프레임이라고 한다. CPU는 연속된 논리주소로 명령을 내리지만 실제 메모리 주소는 연속적이지 않으므로 실제 물리주소로 변환되는 과정이 필요하다. 이때 쓰이는 것이 페이지 테이블이다.
페이징은 프로세스를 물리적으로 나눠서 메모리에 할당하는 방식이라면 세그맨테이션은 프로세스를 논리적 내용을 기반으로 나눠서 배치하는 방식이다. 기본적으로 프로세스를 나눠서 메모리에 할당하는 것은 비슷한데, 세그먼트 크기가 일정하지 않기 때문에 세그먼트 테이블에는 limit 정보가 주어진다. CPU에서 해당 세그먼트의 크기를 넘어서는 주소가 들어오면 인터럽트가 발생해서 프로세스를 강제 종료시킨다.
CPU에서 s, d라는 명령을 보내면 테이블을 통해 a, d로 변환하는데 물리주소 a는 base[s] + d로 계산된다.
예를 들어 논리주소 (2,100) 이 들어오면 a는 base[2] + 100 = 물리주소 4400번이 되고 limit 400인데 사이즈 100이니까 인터럽트 발생 x
논리주소 (1,500) 이 들어오면 base[1]의 limit 400을 넘기는 500이 들어오므로 인터럽트 발생하여 프로세스 강제 종료
페이징은 프로세스 고정 분할 방식으로 외부 단편화를 극복하기 위한 기법이지만 내부 단편화가 존재하며
세그맨테이션은 프로세스 가변 분할 방식으로 내부 단편화를 극복하기 위한 기법이지만 외부 단편화가 존재한다.
세그맨테이션은 보호와 공유에서는 나은 성능을 보이지만, 대부분 요즘은 페이징을 씀. 왜냐면 세그맨테이션에 치명적 단점이 존재하기 때문인데, 이는 프로세스마다 크기가 제각각이고 그때마다 메모리 사이사이 공간이 예측 불가능하게 구멍이 숭숭 뚫려서 다음 프로세스를 적재할 때 어디에 할당하는 것이 최적인지에 대한 알고리즘이 존재하지 않고 기본적으로 외부 단편화가 훨씬 메모리 낭비가 크기 때문이다.
그래서 나온게 세그먼트를 페이징 기법으로 나누는 Paged segmentation인데 이 역시 세그먼트 + 페이징 기법이긴 하지만 주소 변환도 2번해야되는 단점이 있다.
물리 메모리가 가지는 크기의 한계를 극복하기 위해 나온 기술이 가상 메모리이다. 각 프로세스에서 현재 실행에 필요한 부분만 메모리에 적재하여 효율을 극대화하는데 이때 현재 필요한 부분(보통은 페이지)만 올리는 것을 Demading Paging(요구 페이징)이라고 함.
두 프로세스 P1, P2 는 현재 필요한 페이지만 메모리에 할당한 상태인데, 지금은 P1이 수행 중이다. 기존의 페이지 테이블과 다른 점은 Valid bit 를 두어서 현재 메모리에 적재된 부분은 1 , 아닌 부분은 0으로 표시한다.
그러나 CPU가 지금 P1의 3번째 페이지에 접근하려고 하는데 Valid bit 가 0이면 인터럽트가 발생하는데 이런 현상을 ‘페이지 부재’ Page Fault 라고 부른다.
다음과 같은 방식으로 페이지 부재를 처리한다. 즉 페이지 부재가 발생하면 프로세스로 가서 할당되어야 할 페이지를 찾아서 할당해줘야 한다는 의미이다.
이때 페이지 부재 상황에서 페이지 할당하는 방식이 2가지 있다.
프로세스가 맨 처음 실행될 때 어떤 페이지가 필요한지 모르므로 아무 페이지도 할당 안한다. 그러면 시작하자마자 페이지 부재가 발생하고 그때부터 필요한 페이지를 뽑아가는 형식. 메모리를 쓸데 없는 건 아예 안 올리므로 최대한의 효율로 쓰지만 속도는 당연히 느리다.
2) Preparing
위의 방식과 반대되는 개념으로 미리 필요할 것 같은 페이지를 미리 올려두는 것이다. 페이지 부재가 발생할 확률을 줄이므로 속도는 빠르지만, 이미 올라간 페이지가 쓰이지 않는다면 메모리가 낭비되는 단점이 있다.
현실적으로 페이지 부재는 얼마나 발생하는가? 지역성의 원리에 의해 페이지 부재 확률은 실제로 굉장히 낮다. 메모리 접근은 시간적, 공간적 지역성을 가진다.
그럼에도 페이지 부재를 더욱 효율적으로 관리하기 위해 페이지 교체 (Page Replacement) 기법을 사용한다.
위에서 말한 요구 페이징은 그때 그떄 필요한 페이지를 가져온다. 그러나 프로그램들이 계속 실행되면 제아무리 가상 메모리를 쓴다 하더라도 메모리가 페이지들로 가득차게 된다.
이런 상황에서 메모리에 올라가 있는 페이지를 다시 내리고 새로운 패이지를 올리는 과정을 페이지 교체라고 한다.
간단하게 먼저 메모리에 적재된 페이지가 먼저 나가는 알고리즘. 보통 처음에 초기화하는 코드는 프로세스 실행때만 초기화하고 안 쓰이므로 이런 경우에 적절한 방법이다. FIFO는 프레임의 수가 적다면 금방 프레임이 꽉 차서 계속 가장 오래된 놈을 내려야 되므로 페이지 부재가 일어난다. 그러나 프레임의 수가 많을수록 페이지 부재가 적어지며 이를 Belady’s Anomaly라고 부른다.
최적화 알고리즘이란 의미로 가장 사용 안 할 것 같은 페이지를 가장 우선적으로 내려보내는 알고리즘이다. FIFO에 비해 페이지 부재가 감소하긴 하지만 앞으로도 사용안될 것인지에 대한 예측이 보장되지 않으므로 실제로 많이 쓰이지 않는다.
최근에 사용하지 않은 페이지를 가장 먼저 내리는 알고리즘이다. 과거에 많이 안 쓰였다면 미래에도 많이 안 쓰일 것이라는 전제로 쓰인다. 실제로 그런 경우가 많아서 실 사용되는 알고리즘이다.
사용 빈도가 가장 적은 페이지를 교체해준다. LRU와 비슷한 것 같지만 전혀 다른 방식이다. LRU는 시간적으로 가장 오래된 페이지를 교체한다면 LFU는 사용 횟수 자체가 가장 적은 페이지를 교체해준다.
메모리 상의 모든 프로세스 페이지에 대해 교체하는 방식
메모리 상의 자기 프로세스 페이지에서만 교체하는 방식