[CS/OS] 스터디 week 13

2rlokr·2025년 6월 30일

cs-knowledge

목록 보기
12/12
post-thumbnail

1️⃣ 가상 메모리

가상 메모리(Virtual Memory)는 프로세스가 실제 물리 메모리보다 더 큰 메모리를 사용하는 것처럼 보이게 하는 기술이다.
→ 주기억장치(램)의 일부와 보조기억장치(디스크)를 조합해, 논리적 주소 공간을 확장한다.

  • 프로세스 실행에 필요한 일부분만 메모리에 올라가고 나머지는 하드디스크에 올라가게 된다.

가상 메모리가 가능한 이유

1. MMU (Memory Management Unit): 논리 주소 → 물리 주소로 변환
2. 페이징(Paging) 기법: 메모리를 페이지 단위로 나눠 필요한 페이지만 로딩
3. 보조 기억장치와의 연동: 디스크에 일시적으로 데이터를 저장하고 교체 가능

즉, 주소 변환 하드웨어 + 운영체제의 메모리 관리 기법 덕분

❓ MMU(Memory Management Unit)이란?

CPU가 사용하는 가상 주소를 실제 물리 주소로 바꿔주고, 메모리를 보호해주는 메모리 관리 하드웨어로, CPU가 메모리에 접근하기 전에 메모리 주소 번역 작업을 수행한다.

  • CPU는 프로그램을 실행할 때, 가상 주소(논리 주소)만 알고 있기 때문에, 실제 데이터를 찾을 때 물리 메모리를 필요로한다. 이 때, MMU가 사용된다.

❓ 세그멘테이션 방식을 사용하고 있다면, 가상 메모리를 사용할 수 없을까?

사용할 수 있다

세그멘테이션도 가상 메모리 기법 중 하나인데, 페이징과는 다르게 논리 단위(코드, 데이터 등)를 기준으로 나누는 방식이다.

방식이 다를 뿐, 가상 메모리를 사용할 수 있다.

Page Fault 처리 방법

Page Fault란, 프로세스가 요청한 페이지가 메모리에 없을 때 발생하는 인터럽트이다.

처리 순서

  1. CPU가 페이지 폴트 예외(Page Fault Exception)를 발생시킴 (제어권 : 커널)
  2. 운영체제가 디스크에서 해당 페이지를 읽어옴
  3. 메모리에 적절한 위치(빈 페이지 프레임)에 로딩
  4. 필요하면 다른 페이지를 교체 (page replacement, 예: LRU)
  5. 페이지 테이블 갱신
  6. 해당 명령어를 다시 실행 (다시 CPU로 복귀)

페이지 크기에 대한 Trade-Off

페이지 크기 작음페이지 크기 큼
내부 단편화 ↓내부 단편화 ↑
페이지 수 ↑페이지 수 ↓
페이지 테이블 ↑페이지 테이블 ↓
I/O 오버헤드 ↑I/O 효율 ↑
캐시 효율 ↑캐시 효율 ↓

❓ 페이지 크기가 커지면, 페이지 폴트가 더 많이 발생한다고 할 수 있을까?

아니다.

페이지 크기가 커지면, 한번에 많은 데이터를 가져오기 때문에 Page Fault 빈도가 줄어드는 경향이 있다. 하지만, 너무 크면 불필요한 데이터까지 함께 로딩되어 캐시 효율이 떨어지고 내부 단편화가 증가되는 문제가 있다. 또, 지나치게 커진다면 Thrashing을 증가시킬 수 있다.

2️⃣ 세그멘테이션 & 페이징

항목세그멘테이션 (Segmentation)페이징 (Paging)
분할 단위논리적 단위 (코드, 데이터 등)고정 크기 블록 (페이지 단위)
단편화 문제외부 단편화 발생내부 단편화 발생
크기가변 크기고정 크기
주소 구조세그먼트 번호 + 오프셋페이지 번호 + 오프셋
장점논리 구조 반영, 보안 적용 쉬움단순한 구현, 메모리 관리 효율적

페이지와 프레임의 차이

  • Page: 프로세스의 가상 메모리를 일정 크기로 나눈 블록
  • Frame: 실제 물리 메모리(RAM)의 고정 크기 블록

일반적으로 둘의 크기는 같으며 MMU는 페이지 테이블을 사용하여 가상 주소 -> 물리 주소로 변환한다.

단편화

내부 단편화

고정 크기 할당 시, 남은 공간이 낭비되는 문제

  • 할당된 공간에 비해 사용량이 적어서 공간이 낭비되는 현상이다.

외부 단편화

가변 크기 할당 시, 작은 빈 공간이 흩어져서 사용 불가능해지는 문제

  • 여유 메모리 공간이 필요한 메모리 공간보다 크지만, 그 공간들이 조각나있어 연속으로 할당할 수 없는 현상이다.

❓ 페이지에서 실제 주소를 어떻게 가져올 수 있는지 설명해 주세요.

💡 가상 주소 = 페이지 번호 + 오프셋

  • 페이지 번호와 페이지 오프셋이 존재한다.
  • 페이지 번호를 통해 페이지 테이블에서 프레임의 베이스 주소를 알아낸다.
  • 프레임의 베이스 주소와 오프셋을 더해 실제 주소를 가져온다.

변환 과정

  1. 페이지 테이블에서 해당 페이지 번호의 물리 프레임 번호를 찾음
  2. 물리 주소 = 프레임 번호 * 페이지 크기 + 오프셋

❓ 어떤 주소공간이 있을 때, 이 공간이 수정 가능한지 확인할 수 있는 방법이 있나요?

페이지 테이블에 있는 각 엔트리는 해당 페이지에 대한 접근 권한을 비트로 관리한다.

R/W/X 플래그: 읽기(Read), 쓰기(Write), 실행(Execute) 가능 여부
예: 읽기 전용 페이지에 쓰기 시도 → 보호 오류 (Protection Fault) 발생

-> 페이지 테이블을 확인하여 해당 페이지의 접근 권한 정보를 보고 수정 가능 여부를 판단할 수 있다.

❓ 32비트에서, 페이지의 크기가 1kb 이라면 페이지 테이블의 최대 크기는 몇 개일까요?

주소 공간: 32비트 = 2³² = 4GB
페이지 크기: 2¹⁰ (1KB)
→ 페이지 개수 = 2³² / 2¹⁰ = 2²²

❓ 32비트 운영체제는 램을 최대 4G 까지 사용할 수 있습니다. 이 이유를 페이징과 연관 지어서 설명해 주세요.

32비트 주소는 2³² = 4GB까지의 주소만 표현 가능
페이징과 관계: 페이지 번호와 오프셋 합쳐서 32비트 안에 들어야 한다.
따라서 페이지 테이블을 통해 접근할 수 있는 전체 주소 공간은 4GB가 최대

❓ C/C++ 개발을 하게 되면 Segmentation Fault 라는 에러를 접할 수 있을텐데, 이 에러는 세그멘테이션/페이징과 어떤 관계가 있을까요?

  • Segmentation Fault : 프로세스가 허용되지 않은 메모리 영역에 접근할 때 발생한다.
    • 예시: 널 포인터 역참조, 해제된 포인터 접근 등
  • 운영체제는 프로세스의 가상 주소 공간을 세그먼트 또는 페이지로 나눈다.
  • 접근한 주소가 존재하지 않거나, 읽기 전용인데 쓰기를 시도하거나, 해당 영역에 대한 권한이 없을 경우 발생한다.

→ 페이지 테이블 검사 → 보호 위반 → Segmentation Fault 발생

3️⃣ TLB

TLB (Translation Lookaside Buffer) 이란, 가상 주소를 물리 주소로 변환한 결과를 캐싱해두는 고속 메모리이다.

❓ TLB를 쓰면 왜 빨라질까?

📌 주소 변환 속도 향상

  • 가상 주소에서 물리 주소로 변환 할 때, 페이지 테이블을 참조하게 되는데, 이건 느리고 메모리 접근 비용이 크다.

➡ TLB는 이걸 미리 저장해두고 빠르게 꺼내쓰는 캐시이다.

예시

방법속도
페이지 테이블 참조느림 (메모리 접근 필요)
TLB 캐시 히트매우 빠름 (CPU 내부 접근)

➡ TLB hit율이 높을수록 주소 변환이 빨라지고, 전체 성능이 올라간다.

MMU (Memory Management Unit)

MMU란, 가상 주소 -> 물리 주소 변환을 담당하는 하드웨어

MMU의 역할

역할설명
주소 변환가상 주소 → 물리 주소
권한 검사읽기/쓰기/실행 권한 확인
TLB 관리TLB를 사용해서 변환 속도 향상
페이지 폴트 처리 협력페이지 테이블에 없는 주소 → 예외 발생 처리

MMU, TLB 위치

  • MMU는 CPU 안에 있는 하드웨어 장치이다.
  • TLB는 MMU 내부 or CPU 캐시 바로 옆에 위치

❓ 코어가 여러개라면, TLB는 어떻게 동기화 할 수 있을까요?

문제
각 CPU 코어는 자신만의 TLB를 가지고 있기 때문에, 멀티코어 환경에선 TLB의 동기화 문제가 발생할 수 있다.

📌 해결 방법

  • TLB Shootdown : 어떤 코어가 페이지 테이블을 변경하면, 다른 코어들의 TLB에서 특정 가상 주소 매핑을 제거(invalidate)하는 작업
  • OS는 각 코어에 TLB를 비우라는 인터럽트 (IPI)를 보낸다.

❓ TLB 관점에서, Context Switching 발생 시 어떤 변화가 발생하는지 설명해 주세요.

📌 Context Switching이란?
CPU가 다른 프로세스로 전환할 때 발생한다.

📌 TLB 관점 변화
TLB는 이전 프로세스의 가상 주소 → 물리 주소 매핑을 갖고 있다. 여기서 다른 프로세스로 바뀌면 → 기존 TLB 매핑은 잘못된 주소가 된다.

해결 방법
1. TLB Flush (TLB 초기화)

  • OS가 TLB를 전부 비우고, 새 프로세스가 실행되면서 새 주소 매핑이 생기게 된다.

  • 단점: 초기화 후에는 TLB가 비어 있으므로 TLB miss가 증가하게 되고, 결국 페이지 테이블을 자주 조회하게 되어 성능 저하 발생

2. Address Space Identifier (ASID) 사용 (성능 향상)

  • 각 프로세스에 고유 번호(ASID)를 붙이고, TLB가 ASID + 가상 주소를 함께 기억한다.

  • Context Switch 시 TLB를 Flush하지 않아도 되고, TLB 안에 여러 프로세스의 매핑이 공존할 수 있게 된다.

  • 단순히 ASID만 바꾸면, 해당 프로세스의 매핑만 유효하게 작동한다.

➡ 여러 프로세스의 TLB 엔트리를 함께 보관 가능

4️⃣ 동기화 구현

동기화를 구현하기 위한 하드웨어적인 해결 방법

1. Test-and-Set (TAS)

한 번에 0->1로 바꾸는 연산으로, 기존 값을 반환하고, 변경하는 작업이 한 사이클에 이뤄진다. 일반적으로 lock 구현에 사용된다.

// C 스타일 예시
bool test_and_set(bool *target) {
    bool old = *target;
    *target = true;
    return old;
}

2. Compare-and-Swap (CAS)

메모리 값이 기대한 값일 때만 변경 작업이 이뤄지고, 성공/실패 여부를 반환한다.

  • Java의 AtomicInteger.compareAndSet()에 사용된다.
  • Lock-Free 알고리즘 구현의 핵심이다.

3. Memory Barrier / Fence

CPU 명령의 실행 순서를 강제적으로 보장하는 방법이다. 하드웨어 최적화로 예상한 값과 다른 결과가 나올 수 있는 문제를 방지한다.

volatile 키워드 의미

volatile은 변수를 최적화 대상에서 제외하고, 항상 메인 메모리에서 읽도록 강제하는 키워드이다.

volatile boolean flag = true;
  • 컴파일러에게 캐시에 저장하지 말고, 항상 메모리에서 읽으라고 지시
  • 값이 외부 요인(스레드, 하드웨어 등)에 의해 바뀔 수 있음을 의미한다.

❗주의:

  • volatile은 원자성(atomicity)을 보장하지 않는다.
    ex) count++는 여전히 안전하지 않음 → synchronizedAtomicInteger 필요

❓ 싱글코어가 아니라 멀티코어라면, 어떻게 동기화가 이뤄질까요?

멀티코어에서는 각 CPU 코어가 자신만의 캐시를 갖고 있어 문제가 더 복잡하다.

발생 문제
한 코어가 변수 x를 바꿔도 → 다른 코어는 옛날 값을 캐시에서 읽게 된다.

🎯 해결 방법: 캐시 일관성(Cache Coherency) + Memory Barrier

1. 캐시 일관성 프로토콜 (예: MESI)

  • CPU 간 캐시 동기화를 유지
  • 어떤 코어가 메모리를 변경하면 → 다른 코어의 캐시가 무효화됨

2. Memory Barrier (Memory Fence)

  • 컴파일러나 CPU의 명령 재배치를 막고,
  • 읽기/쓰기 순서를 보장

0개의 댓글