메모리 코히어런스(Memory Coherence)는 멀티프로세서 시스템에서 중요한 문제 중 하나이다. 멀티프로세서는 여러 개의 프로세서가 서로 다른 캐시를 가지고 동일한 메모리 공간을 접근할 수 있는 구조로, 이때 데이터 일관성을 유지하는 것이 매우 중요하다. 만약 여러 개의 프로세서가 동일한 메모리 위치를 비일관된 상태로 읽고 쓴다면, 프로그램의 실행 결과는 예측 불가능해지고 오류가 발생할 수 있다.

메모리 코히어런스 정의

메모리 코히어런스는 동일한 메모리 주소에 대한 모든 메모리 연산이 모든 프로세서에 대해 일관된 값을 제공하는 특성이다. 이는 다음과 같은 두 가지 하위 조건을 포함한다:

  1. 일관된 뷰: 모든 프로세서가 어느 시점에서 동일한 메모리 주소를 읽을 때 같은 값을 가져야 한다.
  2. 순서 무결성: 하나의 프로세서에서 관찰되는 메모리 연산의 순서가 다른 모든 프로세서에서 동일하게 관찰되어야 한다.

코히어런스 프로토콜

코히어런스 프로토콜은 메모리 일관성을 유지하기 위해 여러 프로세서가 통신하는 방법을 정의한다. 대표적인 코히어런스 프로토콜로는 MSI, MESI, MOESI 등이 있으며, 각각의 약어는 다른 상태를 나타낸다:

예를 들어, MESI 프로토콜에서 각 캐시 라인은 다음 네 가지 상태 중 하나를 가질 수 있다:

  1. Modified: 캐시에 저장된 데이터가 수정되었고 다른 캐시와 일치하지 않는 상태.
  2. Exclusive: 캐시에 저장된 데이터가 메모리와 일치하지만 다른 캐시에 존재하지 않는 상태.
  3. Shared: 데이터가 여러 캐시에 복사되어 있으며 모든 복사본이 동일한 값을 가질 때의 상태.
  4. Invalid: 캐시에 저장된 데이터가 유효하지 않은 상태.

코히어런스 문제의 예

코히어런스 문제를 이해하기 위해 다음과 같은 시나리오를 생각해 볼 수 있다. 두 개의 프로세서 P_1P_2가 있고, 각각의 캐시에 동일한 주소 X의 데이터를 가지고 있다고 가정한다. 초기 값은 X = 0이라고 하자.

  1. P_1X를 1로 갱신하고, 이를 자신의 캐시에 저장한다.
  2. P_2는 이전에 캐시된X의 값을 0으로 유지한다.
  3. 이 상태에서 P_2X를 읽으면, 여전히 0을 읽게 된다.

이 시나리오에서 P_1P_2의 캐시가 일관된 상태가 아니기 때문에 결과는 올바르지 않는다.

해결 방안

이러한 문제를 해결하기 위해 메모리 코히어런스 프로토콜이 사용된다. 대표적인 해결 방안은 다음과 같다:

이와 같은 프로토콜을 통해 멀티프로세서 시스템에서 메모리 데이터의 일관성을 유지할 수 있다.

디렉토리 기반 코히어런스

디렉토리 기반 코히어런스는 분산된 메모리 시스템에서 데이터 일관성을 유지하기 위한 방법 중 하나이다. 각 메모리 블록에 대해 디렉토리를 유지하고, 이를 통해 어떤 캐시에 블록의 사본이 있는지를 추적한다. 디렉토리는 중앙 집중식 또는 분산식으로 구현될 수 있으며, 다음과 같은 메커니즘을 통해 작동한다:

  1. 스토리지 디렉토리: 모든 메모리 블록에 대해 캐시 상태와 소유권 정보를 저장하는 디렉토리를 유지한다.
  2. 상태 및 소유권 갱신: 프로세서가 특정 메모리 블록을 갱신하거나 접근할 때, 디렉토리는 이를 인지하고 모든 관련 캐시 내의 상태를 갱신한다.

가상 메모리

가상 메모리는 실제 물리적 메모리를 추상화하여 프로세스가 고유한 메모리 주소 공간을 이용하도록 하는 기법이다. 이는 다음과 같은 몇 가지 중요한 목표를 달성한다:

  1. 메모리 보호: 프로세스 간의 메모리 접근을 제어하여 다른 프로세스의 메모리 공간을 침범하지 않도록 한다.
  2. 메모리 확장: 물리적 메모리보다 큰 주소 공간을 이용할 수 있게 하여 더 많은 프로그램 실행을 가능하게 한다.
  3. 효율적 메모리 사용: 실제로 필요한 데이터만 메모리에 적재하여 메모리 자원을 효율적으로 사용한다.

페이징

페이징은 가상 메모리를 작은 고정 크기 블록인 페이지로 나누고, 이를 물리 메모리의 프레임에 매핑하는 방식이다. 페이징에는 다음과 같은 두 가지 주요 구성 요소가 있다:

  1. 페이지 테이블: 가상 주소를 물리 주소로 변환하는 데 사용하는 데이터 구조이다. 각 프로세스마다 별도의 페이지 테이블을 유지한다.
  2. TLB(Translation Lookaside Buffer): 페이지 테이블 접근 속도를 빠르게 하기 위해 자주 사용하는 주소 변환 엔트리를 캐싱하는 고속 메모리이다.

세그멘테이션

페이징과 달리, 세그멘테이션은 가상 주소 공간을 서로 다른 크기의 여러 세그먼트로 나누는 방식이다. 각 세그먼트는 논리적으로 관련된 데이터(예: 코드, 데이터, 스택 등)를 포함한다. 세그멘테이션의 주요 구성 요소는 다음과 같다:

  1. 세그먼트 테이블: 각 세그먼트의 시작 주소와 길이를 저장하는 테이블이다.
  2. 베이스와 리미트 레지스터: 가상 주소를 물리 주소로 변환할 때 사용되는, 세그먼트의 시작 주소와 종료 주소를 보유하는 레지스터이다.

페이지와 세그멘테이션의 혼합

현대 운영 체제는 일반적으로 페이징과 세그멘테이션을 결합한 혼합 모델을 사용한다. 이 모델에서는 세그먼트 단위로 페이지를 나누어 각 세그먼트를 페이징 방식으로 관리한다. 이를 통해 두 기법의 장점을 결합하고, 메모리 보호와 공간 효율성을 동시에 추구할 수 있다.