
2025.04.25
오늘한 내용 : CS - 가상메모리 9.6 ~ 9.8
WEEK07: 시스템 콜, 데이터 세그먼트, 메모리 단편화, sbrk/mmap
가상 메모리 시스템과 캐시 메모리 시스템을 함께 사용함.
이 둘이 올바르게 통합 되어야, 프로그램이 빠르고 안전하게 메모리에 접근할 수 있음.
- CPU는 가상 주소로 메모리 접근을 시도하지만,
- 캐시는 반드시 물리 주소로만 동작
- TLB와 페이지 테이블을 통해 주소 변환을 먼저 수행해야 캐시에 접근할 수 있음
- 가상 주소 기반 캐시는 alias 문제 등으로 인해 거의 사용되지 않음
주소 변환은 매우 자주 발생하는 연산이기 때문에, 빠르게 수행되어야 한다.
하지만 페이지 테이블을 매번 조회하는 건 느리다.
그래서 하드웨어는 TLB(Translation Lookaside Buffer)라는 고속 캐시를 사용해 최근 변환 결과를 저장한다.
| 필드 | 설명 |
|---|---|
| TLBI (Index) | 엔트리를 찾아볼 위치 (VPN의 하위 비트 일부) |
| TLBT (Tag) | 정확한 VPN 식별자 (VPN의 상위 비트) |
| PFN | 변환 결과: 매핑된 물리 프레임 번호 |
| Valid | 엔트리 유효 여부 |
| Perm (RWX) | 접근 권한 (읽기/쓰기/실행) |
CPU → 가상 주소 (VA) 생성
↓
MMU → VPN 추출
↓
TLB 조회:
→ TLBI로 세트 선택
→ TLBT(Tag) 비교
↓
• Hit → PFN 반환 → 물리 주소 계산
• Miss → 페이지 테이블 탐색 → PFN 얻고 TLB 갱신
가상 메모리의 전체 주소 공간을 페이지 단위로 나눈다면, 모든 페이지에 대해 PTE를 만들어야 하고 → 페이지 테이블이 너무 커짐!
그래서 운영체제는 “필요한 부분만 만들어 쓰는” 다중 레벨(계층형) 페이지 테이블을 사용.
| 항목 | 값 |
|---|---|
| 가상 주소 공간 | 48비트 사용 (나머지 16비트는 unused or sign-extended) |
| 페이지 크기 | 4KB = 2¹² → 오프셋(VPO): 12비트 |
| VPN 비트 수 | 48 − 12 = 36비트 |
| PTE 크기 | 보통 8바이트 (64비트 시스템에서는 주소 + 보호 비트 등 포함) |
+------+------+------+------+------------+
| PML4 | PDPT | PD | PT | Offset |
| 9b | 9b | 9b | 9b | 12b |
+------+------+------+------+------------+
Intel Core i7과 Linux는 48비트 가상 주소를 4단계 페이지 테이블로 변환하며, TLB를 통해 고속 주소 매핑을 실현한다.
Core i7 전체 메모리 시스템 구조도
구성요소:
→ TLB는 가상 주소 기반,
→ 캐시는 물리 주소 기반
task_structmm_structpgd: 최상위 페이지 테이블 (PGD) 주소mmap: vm_area_struct의 연결 리스트 시작점vm_area_struct| 필드명 | 설명 |
|---|---|
vm_start | 영역의 시작 가상 주소 |
vm_end | 영역의 끝 가상 주소 |
vm_prot | 접근 권한 (r/w/x) |
vm_flags | 공유/사적 매핑 여부 등 |
vm_next | 다음 영역을 가리키는 포인터 |
task_struct, mm_struct, vm_area_struct는 모두 커널 메모리 안에 존재하며,
프로세스의 가상 주소 공간을 추적하고 관리하는 “관리용 포인터와 메타데이터”일 뿐,
실제 유저의 가상 주소 공간에는 나타나지 않습니다.
메모리 매핑은 디스크 상의 파일을 가상 주소 공간에 직접 매핑하여,
fread나write없이 메모리 접근만으로 파일을 읽고 쓸 수 있게 해주는 기법
- 두 종류의 객체 중 하나로 매핑될 수 있음.
| 구분 | 정규 파일 매핑 | 익명 파일 매핑 |
|---|---|---|
| 지연 페이징 | O (접근 시 디스크에서 해당 파일 페이지 로딩) | O (접근 시 0으로 초기화된 페이지 생성) |
| 데이터 원천 | 디스크에 존재하는 실제 파일 | 존재하지 않는 "빈 파일" 같은 느낌 |
| 초기 페이지 로딩 | 파일 내용으로 채움 | 0으로 초기화(빈 파일처럼 작동) |
| 페이지 폴트 발생 시 | 디스크에서 읽어옴 | 물리 페이지 확보 후 0으로 초기화 |
| 스왑 여부 | O (스왑 공간 사용) | O (스왑 공간 사용) |
| 예시 | 실행 파일, mmap("foo.txt") | malloc(), mmap(NULL, ..., MAP_ANONYMOUS, ...) |
fork() 호출 시,mm_struct, vm_area_struct, 페이지 테이블 등을 부모로부터 복사execve() : 현재 프로세스의 메모리 내용을 싹 지우고, 지정한 프로그램으로 덮어쓰기.execve("a.out", NULL, NULL) 호출 → 현재 프로세스를 a.out 실행 파일로 대체커널에 새 가상메모리 영역을 생성해 줄 것을 요청하는 함수
#include <unistd.h>
#inlcude <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
| 인자 | 의미 |
|---|---|
start | 요청 가상 주소 (NULL이면 커널이 자동 결정) |
length | 매핑할 바이트 수 (페이지 크기 배수) |
prot | 접근 권한: PROT_READ, PROT_WRITE 등 |
flags | 매핑 유형: MAP_SHARED, MAP_PRIVATE, MAP_ANONYMOUS 등 |
fd | 파일 디스크립터 (익명 매핑일 경우 -1) |
offset | 파일의 시작 오프셋 |
가상메모리의 영역들을 삭제하는 함수
#include <unistd.h>
#inlcude <sys/mman.h>
int munmap(void *start, size_t length);