user & kernel address
우리는 메모리에 있는 주소로 모든 영역을 다룬다 x86-64는 8바이트의 가상주소로 ( 하드웨어적 주소는 8바이트로 못 나타냄 ) 페이지 테이블을 통해 실제 하드웨어적 주소로 매핑시킨다.
이러한 8바이트의 가상주소 또한 커널 영역과 유저영역이 존재한다.
CPU 하드웨어 기준: “canonical address” 범위
x86-64는 가상주소가 “아무 64비트 전부”가 아니라, 상위 비트가 특정 규칙으로 부호확장(sign-extension) 되어야 하는 canonical 규칙이 존재한다.
따라서 중간에 큰 hole이 존재
x86-64 4레벨 페이징(48-bit VA) 기준으로 흔히 말하는 경계:
- 로우 half: 0x0000_0000_0000_0000 ~ 0x0000_7fff_ffff_ffff
- 하이 half: 0xffff_8000_0000_0000 ~ 0xffff_ffff_ffff_ffff
- 그 사이가 non-canonical hole 이라 접근하면 예외(예: #GP → General Protection ) 나기 쉬움
리눅스 x86-64 메모리 레이아웃 문서가 이 “hole(48~63비트 sign-extension 때문에 생김)”을 언급하고 있다.
참고: 5레벨 페이징(57-bit VA)이면 이 경계가 또 달라짐 → 페이징 방법에 따라 달라진다.
근데 그러면 궁금할 수 있음
각각의 논리적 코어가 모두 같은 가상주소를 사용하냐?
→ 맞음 모두 같은 가상 주소를 사용하고 있음
그럼 각각의 논리적 코어의 커널 영역은 공유를 하고 있는 건가?
→ 맞음, 따라서 공통적인 자원 접근에 대한 RACE를 막기 위해 세마폴같은게 존재하는 이유
그럼 각각의 논리적 코어에 대해 유저 영역은 어떻게 함? 공유를 할 수도 없는데?
→ 맞음, 공유하면 안됨 이런 경우 같은 프로세스의 스레드 인지 아니면 다른 프로세스의 스레드인지 에 따라 다름
만약 같은 프로세스의 다른 스레드 라면 주소공간(mm)을 공유함으로써 보통 0x401010이 같은 내용을 가리켜. 즉, 같은 페이지 테이블을 가지게 됨
근데 다른 프로세스라면?
프로세스마다 유저 영역은 “페이지 테이블을 갈아끼워서” 바뀌고
서로 다른 프로세스가 똑같이 0x401010 같은 가상주소를 쓰더라도, 그 주소가 가리키는 물리 페이지는 프로세스마다 다를 수 있음
즉, 페이지 테이블을 갈아끼움으로써 유저 영역의 매핑이 완전히 바뀌는것
→ 구현은 CR3 레지스터 값을 바꾸어서 한다고 함
커널 매핑은 왜 공유돼 보이나?
리눅스 x86-64에서는 통상적으로 커널 공간 매핑(커널 텍스트/데이터, direct map, vmalloc 등)이 모든 프로세스의 페이지 테이블에 공통으로 존재해. (대신 U/S 비트로 user mode에서 접근 불가). 이 개요가 x86-64 메모리 레이아웃 문서에 정리되어 있다.
추가 → mitigation
Meltdown 대응으로 들어간 PTI(Page Table Isolation) 가 켜져 있으면, user mode에서 쓰는 페이지 테이블에는 커널 매핑을 대부분 빼버리고, syscall/interrupt로 커널에 들어갈 때 커널용 페이지 테이블로 전환해. 즉 “항상 다 같이 커널 매핑을 공유”가 아니라, user 모드 시점엔 커널이 거의 안 보이게 만드는 모드가 존재해.