메모리는 OS 영역과 User Process 영역으로 구분합니다. User Process는 독립된 메모리 공간을 갖고, 다른 프로세스 메모리 공간에 접근할 수 없습니다. 이를 위해 시작 물리 주소인 Base, 프로세스 크기를 담는Limit Register를 사활용합니다. 즉, 메모리를 참조할 때마다 Base + Limit register 값을 넘는지 체킹합니다. 만약 잘못된 메모리 영역을 접근한다면 Trap Interrupt를 걸어 오류를 막습니다.
프로세스 메모리 주소 결정 시기가 언제냐에 따라 세 가지로 분류합니다.
프로그램을 메모리로 로드시 주소가 결정됩니다. 컴파일 과정에서 메모리 주소를 필요로하는 인스트럭션 주소를 결정하는 것이 아니라, 이런 부분은 표시만 하고 (상대주소) 프로그램 실행을 위해 메모리로 로드되는 시점에 인스트럭션 주소를 확정 짓도록 코드를 수정합니다.
이 방식은 Compile Time Address Binding의 문제를 해결한 것으로 멀티 프로그래밍이 가능합니다. 하지만 메모리에 프로그램을 로드할 때마다 메모리 참조 명령어를 모두 바꿔야 해서 메모리 로딩시 너무 시간이 오래걸리는 단점이 있습니다.
CPU가 해당 인스트럭션을 실행시키는 시점(패치 바로 직전)에 주소를 결정합니다. 아래와 같이 동작하는데 추가 연산으로 인해 속도가 느려 MMU HW 지원을 필요로합니다.
※ MMU (Memory Management Unit)란?
CPU 코어 안에 탑재되어 가상주소를 실제 메모리 주소로 변환 해주는 장치입니다.
프로세스를 빈 공간 중 어디에 넣을지 고민합니다.
시뮬레이션 결과 메모리 측면에서 Worst Fit이 좋지 않고, 속도 측면에서 First Fit이 적합합니다.
프로세스들이 메모리에 적재되고 제거되는 일이 반복되면 프로세스들이 차지하는 메모리 틈 사이에 사용하지 못할 만큼의 작은 자유 공간들이 늘어는데 이를 단편화라고 합니다.
단편화 문제를 해결하고자 Compaction을 이용 하기도 합니다. 이는 작은 공간들을 모두 한곳으로 모으는 것으러 메모리 관점에서는 확실한 방법이지만 작업 효율이 좋지 않습니다. 따라서 보통 Paging, Segmentation을 이용합니다.
하나의 프로세스가 사용하는 메모리가 연속적일 필요는 없는 메모리 관리 방법입니다. 물리 메모리는 Frame이라는 고정 크기로, 프로세스의 논리 공간은 페이지라 불리는 고정크기의 블록으로 구분합니다. 이때 페이지, 프레임 모두 같은 크기로 나누고, 이 둘을 맵핑 시켜주는 page table이 존재합니다. Page Table은 논리 주소 Index와 물리 주소 Index를 맵핑 시켜줍니다. 이 방식은 내부 단편화가 존재하지만 외부 단편화를 해결한 방법으로 메모리 공간을 좀 더 효율적으로 사용할 수 있습니다.
Logical Address는 page number과 offset 값으로 구성합니다. 먼저 page number를 참조하여 물리 메모리의 몇번째 페이지인지를 탐색하고, offset 값으로 해당 페이지 안에서 어느 부분을 참조하는지 파악합니다.
페이징으로 메모리 주소를 접근할 때, 물리 주소를 접근할 때 Page Table을 참조하는데 한 번, 실제 메모리에 접근하는데 한 번을 더하여 총 두 번이 필요합니다. 메모리 접근은 충분한 Overhead로 간주하여 캐싱을 활용한 TLBS를 도입했습니다. 이는 페이지 테이블 정보를 캐시하여 메모리 접근 횟수를 줄입니다. 쉽게 말하면 TLBS는 Page Table Cache라고 생각하면 됩니다.
※ 32bit와 64bit의 차이점
CPU는 레지스터와 함께 데이터를 처리합니다. 32bit 컴퓨터는 레지스터의 크기가 32bit이고, 64bit는 레지스터의 크기가 64bit입니다. 레지스터 크기 차이로 한 번에 데이터를 처리하는 양이 달라집니다. 따라서 64bit 컴퓨터가 데이터 처리 능력이 더 좋습니다. 또한, 레지스터 크기가 달라지면 램 사용량이 달라집니다. 참고로 2^32는 4GB까지 표현 가능하고 그 이상은 표현 불가합니다. 따라서 4GB 이상의 메모리를 쓰는 컴퓨터는 32bit를 사용하지는 못합니다.
같은 크기의 물리적 공간으로 구분하는 것이 아닌, 크기다 다르더라도 논리적 단위로 구별하는 방식을 말합니다. 이에 따라 내부 단편화 문제는 해결했지만 외부 단편화 문제는 존재 합니다. 이때, 세그먼트마다 크기가 달라 다른 프로세스의 영역을 침범할 가능성이 높아 에러체크는 필수입니다. 에러체크는 Segment Table을 통해 진행합니다. (base : 항목 별 세그먼트 시작 주소 / Limit : 항목별 세그먼트 길이)
예를 들어, 개발자는 자신의 프로그램을 물리적 특성이 강한 페이지로 인식하기 보다는 함수는 함수로, 클래스는 클래스로 즉 모듈별로 인식합니다. 또한 같은 프로그램을 사용하고 내부 데이터가 다른 프로세스를 사용하는 경우에서는 필요 부분의 세그먼트 공유가 가능하여 메모리를 효율적으로 사용할 수 있습니다. 또한 데이터, 힙 영역과 같이 주요 데이터는 '정보 보호'를 통해 보안에 신경 쓸 수 있습니다.
즉, 의미있는 단위로 나누게 되어 정보 보호와 정보 공유의 이점이 존재합니다.
현재는 Sementation과 Paging을 혼합하여 각각이 가진 장점을 활용합니다.
먼저 프로세스를 세그먼트 단위로 잘라, 의미있는 단위로 구분합니다. 이에 따라 '정보 보호', '공유'의 이점이 생깁니다. 하지만 외부 단편화 문제가 생길 수 있습니다. 따라서 우리는 잘라진 세그먼트를 다시 페이지 단위로 구분하는 페이징을 취합니다. 이 경우 외부 단편화 문제 또한 해결할 수 있습니다. 하지만 테이블을 두번 거쳐 속도의 단점이 발생합니다.