운영체제의 핵심적인 두가지 역할을 프로세스 관리와 메모리 관리라고 한다.
프로세스 관리는 CPU 스케줄링과 동기화를 통해서 배웠고
메모리는 관리는 오늘 정리해보려 한다.
프로세스 A를 A의 크기만큼 메모리 주소에 할당받아 연속적으로(그냥 뭉텅이로 한번에) 배치하고 프로세스 B는 프로세스 A 이후에 또 B 크기만큼 메모리 주소를 할당받아 배치하는 방식을 "연속 메모리 할당"이라고 한다.
입출력 요구로 대기 상태가 된 프로세스, 오랫동안 사용하지 않는 프로세스 등 메모리에 적재된 프로세스 중에서 현재 실행되지 않는 프로세스가 있을 수 있다.
이 프로세스를 보조기억장치로 옮겨 새로 생긴 메모리 공간에 또 다른 프로세스를 적재하는 방식을 "스와핑"이라고 한다.
프로세스가 메모리 내 빈 공간에 적재될 때 연속적으로 할당하는 방식
연속적으로 할당하면 빈공간이 낭비될 수 있다.
예를 들어
위와 같은 상황일 때 남은 공간은 50MB이나 50MB 크기의 프로그램을 적재할 수 없는 상황이 발생한다.
이처럼 분명 바깥에서 볼 때는 빈공간이지만 그 공간만큼의 프로세스를 적재하기 어려워 공간이 낭비되고 있다. 이를 외부 단편화라고 한다.
메모리 조각 모음이라고도 불리는데 여기저기 흩어져 있는 빈공간을 하나로 모으는 방식
단, 모으는 동안 시스템은 하던 일을 중지 + 메모리에 있는 내용을 옮기는 작업에서의 오버헤드 + 또한 오버헤드를 적게 발생시키기 위한 명확한 해결방안을 결정하기 어려움
프로세스를 메모리에 연속으로 할당하는 방식의 두가지 문제
이를 해결해주는 "가상 메모리" 기술은
실행하고자 하는 프로그램을 일부만 메모리에 적재하여 실제 물리 메모리 크기보다 더 큰 프로세스를 실행할 수 있게 하는 기술이다.
가상 메모리 관리 기법은 크게
페이징과 세그멘테이션이 있고 페이징에 대해서 알아본다.
프로세스를 일정한 단위로 잘라서 이를 메모리에 불연속적으로 할당하는 것
이로써 외부단편화의 문제를 해결하고 물리 메모리보다 큰 프로세스를 실행할 수 있다.
즉 페이징이란
프로세스의 논리 주소 공간을 페이지라는 일정한 단위로 자르고, 메모리 물리 주소 공간을 프레임이라는 페이지와 동일한 크기의 일정한 단위로 자른 뒤 페이지를 프레임에 할당하는 가상 메모리 관리 기법이다.
이를 통해서 물리메모리보다 더 큰 프로세스를 실행할 수 있다.
모든 프로세스가 페이지 크기에 딱 맞게 잘라지지 않는다.
만약 페이지 크기가 10이라고 할 때
108의 크기를 갖는 프로세스는 마지막 8에서 2의 메모리 공간이 낭비된다.
그렇다면 페이지의 크기를 작게 만들자 -> 페이지 테이블의 크기가 커짐
따라서 적절한 크기의 페이지를 조정하는 것이 중요하다.
프로세스가 불연속적으로 메모리에 배치된다
하지만 CPU는 프로세스를 이루는 페이지가 어느 프레임에 적재되어 있는지 알기 어렵다.
"페이지 테이블"을 이용해서 어떤 페이지가 어떤 프레임과 연결되어 있는지를 저장한다.
즉 물리 주소 상에 분산되어 저장되어 있어도 CPU는 페이징 테이블을 통해서 연속적으로 보이게 된다.
(Page Table Base Register : PTBR)
프로세스마다 각자의 프로세스 테이블을 가지고 있고 프로세스 테이블은 메모리에 적재된다.
따라서 CPU의 페이지 테이블 베이스 레지스터 이하 PTBR은 각 프로세스의 페이지 테이블이 존재하는 메모리의 주소를 향하고 있다.
프로세스 A가 실행될 때 PTBR은 프로세스 A의 페이지 테이블을 가리키고 CPU는 프로세스 A의 페이지 테이블을 통해서 프로세스 A의 페이지가 적재된 프레임을 알 수 있다.
즉 CPU는 메모리에 2번 접근하다.
앞에서도 배웠지만 CPU가 메모리에 접근하는 시간이 꽤 걸린다.
따라서 중간에 캐시 메모리를 두고 캐시 메모리가 참조지역성의 원칙에 따라 CPU가 읽어올만한 메모리의 내용을 미리 저장하고 있는데
이는 페이징에서도 이용된다. 바로 TLB이다.
CPU와 메모리 사이에 TLB라는 캐시 메모리를 두고 여기에 CPU가 참조할 만한 페이지 테이블의 내용을 저장한다.
참조지역성의 원칙에 따라 저장하며
만약에 찾고자 하는 페이지 테이블의 내용이 있다면 TLB 히트
없다면 TLB 미스라고 한다.
주소지장방식을 기억해야 한다.
CPU는 논리주소를 통해 메모리의 물리 주소를 접근한다.
실제 프로세스는 메모리 물리 주소에 저장된다는 것을 떠올려야 한다.
페이지나 프레임은 일정한 단위로 나누어진 프로세스이기 때문에 당연히 여러 주소를 포괄하고 있다.
0,1 이렇게 하나의 단위가 아니라 0~10, 11~21 이런 식으로 여러 주소가 포함된다.
따라서 모든 페이징 시스템에서 논리주소는
페이지 번호와 논리 주소로 이루어져 있다.
CPU가 논리주소로 메모리의 특정주소에 접근하는 과정은
<페이지 번호, 변위> -> TLB -> <프레임 번호, 변위>
논리 주소 -> TLB -> 물리 주소
페이지 테이블의 각각 행들을 페이지 테이블 엔트리라고 한다.
이 페이지 테이블 엔트리에는
기억할 것
프로세스마다 하나의 페이지 테이블을 가지고 있다.
유효 비트는 현재 해당 페이지가 메모리에 적재되어 있어 접근이 가능한지(1) 아니면 보조기억장치에 있어 접근이 불가능한지(0)를 나타낸다.
만약 접근이 불가능한 0의 상태라면 페이지 폴트라는 예외 상황이 발생한다.
CPU가 페이지 폴트를 처리하는 과정
Read r , Write w, eXecute x의 조합으로 이루어져 있다.
그 페이지를 읽어도, 써도 , 실행해도 되는지를 각 1로 나타내고 권한이 없으면 운영체제가 막는다.
CPU가 페이지에 접근한 적이 있는지의 여부
메모리에 페이지를 적재한 후에도 CPU가 이를 접근한 적이 있는지를 나타낸다.
CPU가 해당 페이지에 데이터를 쓴적이 있는지(1)를 타나낸다.
만약에 썼다면 스왑 아웃되는 과정에서 보조기억장치에 변경된 내용을 기록하는 작업이 추가되어야 한다.
fork는 부모 프로세스가 자식 프로세스를 만들 때 자기 자신을 그대로 복제헤 메모리에 적재하는 방식이다.
위 방법은 복사 작업이 필요하고 이는 프로세스 생성 시간을 늦추고 불필요한 메모리 낭비를 야기한다.
하지만 이 방법 말고도 프로세스를 통째로 메모리에 중복 저장하지 않으면서 프로세스끼리 자원을 공유하지 않는 방법도 있다. 이는 페이징을 통해서 가능하다.
자식 프로세스와 부모 프로세스는 각 페이지 테이블을 가지고 있지만
자식 프로세스의 페이지 테이블은 부모 프로세스의 프레임과 맵핑된다.
단 조건은 그저 읽기만 하는 경우이다.
읽기만 하는 경우 위와 같이 메모리에 적재된 것은 부모 프로세스의 페이지이다.
그러나 자식 프로세스가 부모 프로세스와 다른 내용을 페이지에 쓴다면
메모리에 새롭게 적힌 자식 프로세스의 페이지가 생성된다.
계층적 페이징은 페이지 테이블을 페이징하는 것
프로세스가 커지면 페이지 테이블도 함께 커진다. 프로세스를 이루는 모든 페이지 테이블을 메모리에 적재하는 것은 메모리 낭비를 불러온다. 따라서
프로세스도 페이지로 나눠서 저장하듯 페이지 테이블도 나눠서 저장하는 것이다.
즉 잘 안쓰는 페이지 테이블은 보조기억장치에 넣어둔다.
위와 같이 페이지 테이블을 쪼개고
바깥 페이지 테이블은 그 쪼갠 테이블의 계층적 번호를 저장하고 있는다.
따라서 메모리에 적재되는 것은
바깥 페이지 테이블, 그리고 쪼개진 페이지 테이블이다.
(바깥 페이지 테이블은 항상 메모리에 적재되고 쪼개진 페이지 테이블은 CPU의 사용에 따라 스와핑이 진행된다.)
따라서 CPU의 논리주소도 구조가 바뀌게 된다.
원래는 페이지 번호와 변위값으로 저장되어 있었다면 위와 같이 바깥 페이지의 번호, 안쪽 페이지의 번호, 변위값으로 구분된다.
정리하면
계층은 2,3,4, 그 이상으로 구성될 수 있다.
하지만 페이지 테이블의 계층이 늘어날수록 페이지 폴트의 예외가 발생핼 때 메모리 참조 횟수가 많아진다는 것을 기억하자!
운영체제가 메모리에 프로세스를 할당하는 방식에는