CS 공부/운영체제

OS 정리 5 - 메모리 관리 (Memory Management) [1]

1. Background

  • 프로그램은 (디스크에서) 메모리로 가져 와서 프로세스 내에서 실행되도록 해야 함
  • 주 메모리 및 레지스터는 CPU가 직접 액세스 할 수 있음
  • 레지스터는 하나의 CPU 클록 (또는 그 이하)으로 액세스
  • 주 메모리는 많은 사이클을 수행할 수 있음
  • 캐시는 주 메모리와 CPU 레지스터 사이에 위치
  • 올바른 작동을 보장하기 위해 필요한 메모리 보호

 

2. Binding of Instructions and Data to Memory

프로그램은 기본적으로 이진 실행파일 형태로 디스크에 저장되며, 이를 실행하기 위해서는 프로그램을 메모리로 가져오는 것이 필요하다. 이때, 주소 바인딩은 크게 세가지 단계로 이루어진다.

 

1. 컴파일 시간 바인딩
만일 프로세스가 메모리 내에 들어갈 위치를 컴파일 시간에 미리 알 수 있으면 컴파일러는 절대 코드를 생성할 수 있음
위치 변경을 시작하면 코드를 다시 컴파일 해야 함


2. 적재 시간 바인딩
컴파일 할 때 메모리 위치를 알 수 없는 경우 재배치(reallocatable) 가능한 코드를 생성


3. 실행시간 바인딩
프로세스가 실행하는 중간에 메모리 내의 한 세그먼트로부터 다른 세그먼트로 옮겨질 수 있다면 바인딩이 지연됨
주소 맵 (예 기준 및 상한 레지스터)에 대한 하드웨어 지원 필요

주소 바인딩 과정

 

3. Logical vs Physical Address

논리적 주소(Logical Address) : CPU에 의해 생성되는 주소를 일반적으로 논리적 주소라 한다. (= 유저 프로세스가 사용중인 주소)

물리적 주소(Physical Address) : 메모리 주소 레지스터에 표시되는 주소를 물리적 주소라 한다.

 

이때, 논리적 주소와 물리적 주소는 바인딩 과정이 다르며, 논리적 주소는 사용되기 전 반드시 물리적 주소로 변환되어야 한다.

그렇기 때문에 실행시간동안 가상주소를 물리적주소로 변환하는 과정이 필요한데, 이때 우리는 MMU(Memory-Management Unit)를 사용한다. 

MMU를 사용한 주소 변환

해당 과정에서 relocation register는 base register의 역할을 하고, 사용자 프로세스에 의해 생성되는 모든 주소들에 Relocation 레지스터의 값을 더해 실제 메모리 주소를 구한다.

 

따라서, 사용자 프로그램은 실제 메모리 주소를 확인하는 것이 불가능하게 된다.

 

 

4. Swapping

프로세스는 실행되기 위해서 반드시 메모리 위에 있어야 하는데, 한 프로세스가 CPU 할당 시간이 끝나면 주 메모리 관리자가 이 프로세스를 보조 메모리로 교체하여 내보내고 다른 프로세스를 메모리로 불러들이는 과정을 바로 Swapping이라고 한다.

 

* Backing store(백업 저장소) : 모든 사용자의 모든 메모리 이미지 사본을 수용 할 수 있을 만큼 큰 고속 디스크

 

스와핑 예제

 

4. Memory Allocation

메모리는 일반적으로 OS(운영체제)를 위한 부분과 사용자 프로세스를 위한 부분으로 나뉜다. 메모리를 할당하는 가장 단순한 방법은 메모리를 고정된 크기의 파티션(Partitions)으로 나누는 것이다. 각 파티션은 단 하나의 프로세스를 포함한다. 따라서, 멀티프로그래밍의 정도는 파티션의 개수에 따라 정해진다.

 

이러한 할당의 과정에서 free memory의 block이 생기는 것을 hole이라고 하고, 메모리는 여러가지 크기의 hole을 포함하게 된다. 따라서, 운영체제는 프로세스들의 메모리 요구량에 따라 어떤 프로세스가 어떤 사용가능한 메모리 공간에 할당될지 결정해야 한다. 만약 프로세스가 할당된다면, CPU 사용에 대해 다른 프로세스와 경쟁할 수 있을 것이다.

 

이러한 allocation 문제의 해결책으론 Dynamic Storage Allocation Problem과 연결되는데, 이는 다음의 세가지이다.

  • 최초 적합(First Fit) : 요구되는 메모리 크기보다 충분히 큰, 가장 처음 만나는 hole을 할당한다.
  • 최적 적합(Best Fit) : 요구되는 메모리 크기보다 큰 hole들 중 가장 작은 크기의 hole을 할당한다.
  • 최악 적합(Worst Fit) : 가장 큰 hole을 할당한다.

 

5. Fragmentation

이러한 적합 할당을 하다보면 단편화 현상이 일어나는데, 이는 크게 두가지로 나뉜다.

  • 외부 단편화 : 유휴 공간들을 모두 합치면 충분한 공간이 되지만 너무 작은 조각들로 여러 곳에 분산되어 있을 때 발생
  • 내부 단편화 : 할당된 공간은 요구 공간보다 더 클 수 있음. 분할 내부에 존재하고 있으며, 현재 사용되고 있지 않은 메모리

이중 외부 단편화를 해결하는 방법으로는  Compaction이 있다. 할당된 메모리를 재배치하여 작은 크기의 hole들을 한 데 모아 보다 큰 hole을 만드는 것이 목적이다. 하지만 메모리의 재배치가 실행 시간에 동적으로 동작할 경우에만 가능한 제한적인 방법이다.

 

다른 가능한 방법으로는 프로세스들의 논리적 주소공간을 비연속적(Noncontiguous)이 되도록 하여 사용 가능한 물리적 메모리 어디든 할당될 수 있도록 하는 것이다. 이를 위해 Paging, Segmentation 기법이 사용된다.

 

6. Paging

페이징이란 프로세스가 차지하는 물리적 메모리 공간이 비연속적이 될 수 있도록 허용하는 메모리 관리 기법을 말한다. 페이징의 경우에는 외부 단편화가 이루어지지 않지만, 내부 단편화는 발생할 수 있다는 특징이 있다. 또한, 현재 대부분의 운영체제에서 활용되는 방법이기도 하다.

 

이러한 페이징을 구현하는 기본적인 방법으로는 물리적 메모리를 고정된 크기의 프레임(Frame)으로 나누고, 논리적 메모리를 동일한 크기의 페이지(Page)로 나누는 것이 있다.

 

CPU에 의해 생성된 모든 주소들은 페이지 번호(Page Number, p)와 페이지 간격(Page Offset, d)로 나뉘며, 페이지 번호는 페이지 테이블의 인덱스로써 사용된다. 

주소들의 할당 예시
기본적인 페이징 하드웨어 구현

 

이런 페이지 테이블은 메인 메모리에서 관리되는데, 이를 하드웨어적으로 구현하는 방법은 다음과 같다.

 

페이지 테이블 기준 레지스터(PTBR - 페이지 테이블을 가리킴
페이지 테이블 길이 레지스터(PRLR) - 페이지 테이블의 크기를 나타냄

 

6.1 TLB

다만 이는 사용자 메모리에 접근하는데 걸리는 시간에 있어서 문제가 발생할 수 있기 때문에, TLB(Translation Look-aside Buffer)를 사용해 cache 메모리를 활용하여 문제를 해결한다.

TLB 활용 예시

TLB는 적은 수의 테이블에 대한 정보를 가지고 있는 캐시 메모리로, 해당 페이지 번호가 TLB에 있다면 TLB Hit가 발생해 즉시 메모리에 접근하도록 하며, TLB에 없다면 TLB Miss로 판단해 페이지 테이블에서 진행하게 된다.

 

이때, 특정 테이블 번호가 TLB에서 발견될 확률을 Hit Ratio라고 하며, 이를 높이면 성능적으로 긍정적인 효과를 볼 수 있다.

 

6.2 Memory Protection

페이징을 사용하는 곳에서 범위를 확인하고 메모리를 보호하기 위해서는 protection bit를 활용할 수 있다. 이때, Valid-Invalid bit를 추가하는 방식으로 주로 활용되는데, valid bit라면 해당 페이지가 유효한 페이지임을 나타낸다. 

valid, invalid 표시 예제

 

6.3 Shared pages

예를들어 libc 와 같이 대체로 많이 쓰이는 라이브러리와 같은 내용을 공유하면서 효과적으로 사용하는 것을 의미한다.

공유 페이지 예제