한 문장 요약

컨텍스트 스위치 시 다른 프로세스의 매핑이 달라서 TLB를 비워야 함.

다만 x86-64에서는 PCID(Process Context ID)를 사용하면 프로세스별로 TLB 엔트리를 구분해서 전부 비우지 않아도 됨

mmap/munmap으로 매핑이 바뀌면 해당 엔트리만 무효화

배경

가상 메모리(Virtual Memory)와 페이징(Paging)

현대 운영체제는 각 프로세스에게 "독립적인 메모리 공간"을 제공하기 위해 "가상 메모리"라는 개념을 사용.

개발자가 코드에서 다루는 "Ox12345678" 같은 주소는 실제 물리 메모리가 아닌 가상 주소.

운영체제는 이 가상 주소를 실제 물리 주소로 변환.

이때 메모리를 일정한 크기로 잘라서 관리하는데, 이 단위를 "페이지(Page)"라 함.

즉, 운영 체제는 "A 프로세스의 1번 가상 페이지는 물리 메모리의 100번 프레임에 있다"와 같은 정보를 페이지 테이블에 기록.

CPU의 MMU(Memory Management Unit)는 이 페이지 테이블을 참조하여 주소 변환을 수행

TLB : 주소 변환을 위한 고속 캐시

그런데, 메모리에 접근할 때마다 매번 페이지 테이블을 뒤지는 것은 비효율적.

페이지 테이블 자체도 메모리에 있기 때문에, 주소 변환을 위해 추가적인 메모리 접근이 여러 번 발생할 수 있음.

이 속도를 높이기 위해 MMU 내부에 작고 빠른 캐시를 두었는데, 이것이 바로 "TLB(Translation Lookaside Buffer)"임. TLB는 최근에 변환된 "가상 주소-물리 주소" 매핑 정보를 저장
메모리 접근 과정
---------------
1. CPU
	- 특정 가상 주소의 데이터 요청
	
2. MMU
	- TLB 확인(Cache Hit)
		: TLB에 매핑 정보가 있으면, 즉시 물리 주소로 변환하여 메모리에 접근(매우 빠름)
	- 페이지 테이블 확인(Cache Miss)
		: TLB에 정보가 없으면, 페이지 테이블을 뒤져서 매핑 정보를 찾음(느림)
	- 페이지 폴트(Page Fauilt)
		: 페이지 테이블에도 정보가 없으면(물리 메모리에 없음), 디스크에서 해당 데이터를
			가져온 후 주소 변환을 재시도(매우 매우 느림)
=>
즉, TLB는 주소 변환의 성능을 좌우하는 핵심 장치

TLB의 구조와 컨텍스트 스위칭의 문제

image.png

과거에는 이 PCID가 없었음. 그로 인해서 "이 TLB 엔트리가 어떤 프로세스의 것인지 알수가 없는 문제가 발생"

아래와 같은 문제 발생
--------------------
1) 정보 유출(Confidentiality)
 - A의 가상 주소 "0x400000(VPN=X)"이 물리페이지 PFN=100에 매핑돼 있었고, 거기에 비밀번호/
	 토큰 같은 데이터가 있다고 가정
 - 컨텍스트 스위치로 B로 넘어왔는데 "TLB flush/태그"가 없어서 TLB에 "(VPN=X -> PFN=100)"이
	 남아있음
 - B도 우연히 같은 주소 "0x400000"을 읽으면 "TLB hit"로 PFN=100을 읽게 되어 "A의 데이터를
	 B가 읽는" 꼴이 됨.

해결 방법
---------
1) TLB flush
	- 컨텍스트 스위치 때 TLB를 비워서 A의 엔트리를 제거
2) 태그 방식(PCID/ASID)
	- TLB 엔트리에 "이건 PCID=11의 주소공간의 것"이라고 표식
	=> B(PCID=12)에서는 같은 VPN이라도 hit로 인정되지 않음

최적화 - ASID, PCID

위 비효율을 해결하기 위해 "태그" 개념이 도입.

ASID(Address Space ID), 운영체제(리눅스) 수준에서 프로세스의 메모리 공간을 구별하기 위해 부여하는 ID.
PCID(Process-Context ID), 하드웨어(CPU) 수준에서 ASID와 유사한 개념을 구현한 것. "TLB 트리에 이 PCID 태그를 붙여서," 어떤 프로세스에 속한 매핑 정보인지를 명시

이로 인해서 컨텍스트 스위칭이 일어나도 TLB를 전부 비울 필요가 없음. 그냥 "지금부터는 PCID 11인 엔트리만 사용해"라고 CPU에게 알려주기만 하면 됨. 덕분에 다른 프로세스의 TLB 엔트리는 그대로 유지되고, "나중에 그 프로세스로 다시 돌아왔을 때 캐시를 재사용할 수 있게되었음"

배경

[컨텍스트 스위치 발생: Process A -> Process B]
          |
          V
[choose_new_asid()]
  - Process B를 위한 ASID/PCID 할당
  - 이 PCID가 재사용되어 이전 내용이 더럽혀졌나? (need_flush 결정)
          |
          +----------------------+
          |                      |
 (need_flush == true)    (need_flush == false)
          |                      |
          V                      V
[build_cr3()]            [build_cr3_noflush()]
  - CR3 값 생성           - CR3 값 생성
  - (bit 63 = 0)         - (bit 63 = 1)
          |                      |
          V                      V
[write_cr3(new_value)]   [write_cr3(new_value)]
          |                      |
          V                      V
[하드웨어 동작]             [하드웨어 동작]
  - MOV to CR3            - MOV to CR3
  - CR4.PCIDE=1, bit63=0   - CR4.PCIDE=1, bit63=1
  - "해당 PCID의 TLB       - "TLB 플러시 안 함!"
    엔트리를 플러시!"