Step 69. 가상 메모리 — 모든 프로세스의 달콤한 착각

Step 69. 가상 메모리 — 모든 프로세스의 달콤한 착각

Level 1 — 프로그래밍과 컴퓨터 내부 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 68을 마쳤다. fork로 프로세스가 복제되고 메모리가 사본으로 넘어간다는 것을 실험으로 확인했다. 포인터와 주소의 개념(Step 58~60)을 안다.

  • 준비물: 리눅스 터미널(WSL 또는 우분투)과 gcc. 오늘은 지금까지 배운 것 중 가장 추상적인 주제지만, 실험을 네 개나 준비했으니 손으로 만져 가며 이해해 봅시다.
  • 주의: 오늘 실습은 100% 안전합니다. 큰 메모리를 "할당만" 해 보는 실험이 있는데, 실제로 그 메모리를 쓰지는 않으므로 컴퓨터가 느려지지 않습니다.

지난 시간에 우리는 fork로 프로세스를 복제하며 "각자 독립된 메모리를 갖는다"고 배웠습니다. 그런데 곰곰이 생각하면 이상한 점이 있습니다. 여러분이 만든 프로그램은 변수의 주소를 출력했고, 그 주소는 대략 0x7fff… 근처였습니다. 그런데 제 컴퓨터에서 같은 프로그램을 돌려도 비슷한 주소가 나옵니다. 컴퓨터도 다르고 RAM도 다른데, 어째서 주소가 비슷할까요? 정답은, 그 주소가 진짜 RAM의 주소가 아니기 때문입니다. 모든 프로세스는 "나 혼자 이 컴퓨터의 메모리를 전부 쓰고 있다"고 믿게 해 주는 거대한 착각 속에 살고 있고, 이 착각을 만들어 주는 장치가 오늘의 주인공 가상 메모리(virtual memory)입니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 가상 주소와 물리 주소의 차이, 그리고 둘을 잇는 페이지 테이블의 역할을 설명한다
  • "같은 가상 주소 ≠ 같은 물리 메모리"를 실험으로 확인한다
  • /proc/self/maps로 프로세스의 가상 주소 지도를 읽는다
  • 지연 할당(lazy allocation)이 무엇인지 malloc 실험으로 설명한다
  • 가상 메모리가 왜 프로세스 격리라는 보안 성벽인지 말한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 C 언어 + 리눅스 터미널 (WSL 우분투 24.04, RAM 7.5GB에서 실측)
오늘의 명령어 cat /proc/self/maps(가상 주소 지도), free -h(물리 메모리와 스왑), getconf PAGESIZE(페이지 크기), setarch -R(ASLR 끄고 실행), tr/cut/uniq -c(권한 문자 세기)
오늘의 C 요소 &변수로 주소 출력, %p 서식, malloc()으로 큰 메모리 요청
필요한 개념 가상 주소/물리 주소, 페이지(4KB), 페이지 테이블, MMU, 스왑, ASLR
오늘의 산출물 가상 메모리 그림 에세이 — 두 프로세스의 주소 공간과 RAM의 연결 그림

2-1. 두 종류의 주소

오늘 배울 핵심 용어는 딱 두 개입니다.

  • 가상 주소(virtual address): 프로세스가 보고 쓰는 주소. 프로그램 안의 포인터 값은 전부 이것입니다. 각 프로세스마다 0번지부터 시작하는 자기만의 주소 공간입니다.
  • 물리 주소(physical address): 실제 RAM 칩 위의 진짜 위치. 이것은 컴퓨터에 하나뿐입니다.

식당에 비유해 봅시다. 가상 주소는 "테이블 번호"이고 물리 주소는 "주방의 실제 조리대 위치"입니다. 손님(프로세스)은 3번 테이블만 알면 되고, 그 3번이 주방 어디와 연결되는지는 식당 직원(운영체제와 CPU)만 압니다. 두 테이블 손님이 각자 "3번"이라고 불러도, 서로 다른 조리대에서 음식이 나올 수 있는 것입니다.

2-2. 페이지와 페이지 테이블

가상 주소를 물리 주소로 바꾸는 작업은 한 바이트씩 하면 표가 너무 커지기 때문에, 메모리를 일정 크기로 잘라서 변환합니다. 이 조각이 페이지(page)로, 오늘 실측할 컴퓨터에서는 4096바이트(4KB)입니다. 그리고 "가상 페이지 몇 번은 물리 페이지 몇 번과 연결된다"를 적어 둔 대조표가 페이지 테이블(page table)입니다. 이 대조표는 프로세스마다 따로 있어서, 같은 가상 주소도 프로세스 A에서는 RAM의 이쪽으로, 프로세스 B에서는 저쪽으로 연결됩니다.

변환 자체는 CPU 안의 MMU(메모리 관리 장치)라는 하드웨어가 번개같이 해 줍니다. 프로그램은 변환이 일어나는지조차 모릅니다. 그냥 주소를 찌르면 MMU가 알아서 진짜 자리를 찾아 주는 것입니다.

2-3. 이 장치가 만드는 세 가지 기적

  1. 격리: 프로세스 A의 페이지 테이블에는 프로세스 B의 물리 페이지가 아예 적혀 있지 않습니다. 그래서 A는 아무리 이상한 주소를 찔러도 B의 메모리에 닿을 수 없습니다. Step 67의 세그멘테이션 폴트도 "그 가상 주소는 네 페이지 테이블에 없다"는 MMU의 거절이었습니다.
  2. 공유: 반대로, 두 프로세스의 페이지 테이블이 같은 물리 페이지를 가리키게 하면 메모리를 나눠 쓸 수 있습니다. C 표준 라이브러리는 모든 프로세스가 쓰니까, 물리 메모리에는 한 벌만 올려 두고 모두의 페이지 테이블에 등록합니다.
  3. 착각의 확장: RAM이 부족하면 당분간 안 쓰는 페이지를 디스크에 잠시 보냈다가(이것이 스왑, swap), 필요해지면 다시 불러옵니다. 프로세스는 자기 메모리 일부가 디스크에 갔다 온 줄도 모릅니다.

3. 따라 하기

3-1. 예측해 보기 — 같은 프로그램, 두 번 실행

간단한 주소 출력 프로그램을 만듭니다.

입력 (addr.c)

#include <stdio.h>

int global_var = 42;

int main(void) {
    int local_var = 7;
    printf("전역 변수 주소: %p\n", (void*)&global_var);
    printf("지역 변수 주소: %p\n", (void*)&local_var);
    return 0;
}

실행할 때마다 주소가 같을까요 다를까요? 그리고 전역 변수와 지역 변수의 주소, 둘의 모양은 서로 어떻게 다를까요? 예측을 적어 두세요.

입력

gcc -o addr addr.c
./addr
./addr

출력 (2026-09-09 실측):

== 1회차 ==
전역 변수 주소: 0x5b573eb02010
지역 변수 주소: 0x7ffca2c9f354
== 2회차 ==
전역 변수 주소: 0x5eb1d06a2010
지역 변수 주소: 0x7ffe4b3990b4

읽는 법: 두 가지를 읽어야 합니다. 첫째, 전역 변수 주소의 끝자리 2010은 두 번 모두 같습니다. 프로그램 안에서의 상대적 자리는 매번 똑같다는 뜻입니다. 둘째, 앞쪽은 0x5b57…에서 0x5eb1…로 바뀌었습니다. 운영체제가 실행할 때마다 지도의 시작점을 무작위로 옮기기 때문입니다(ASLR — Step 70에서 자세히 다룹니다). 그리고 지역 변수(스택)는 0x7ff…라는 전혀 다른 높은 대역에 있습니다.

: "주소가 바뀌든 말든 프로그램은 멀쩡히 돈다"는 사실 자체가, 이 주소가 진짜 RAM 좌표가 아니라는 첫 번째 힌트입니다.

3-2. 주소 고정 실험 — 같은 가상 주소의 증명

이번에는 시작점 옮기기(ASLR)를 끄고 두 번 실행해 봅니다. setarch -R은 "이 프로그램만 무작위 배치 없이 실행해 달라"는 명령으로, 그 프로그램 하나에만 적용되고 시스템 설정은 바뀌지 않습니다.

입력

setarch -R ./addr
setarch -R ./addr

출력 (2026-09-09 실측):

전역 변수 주소: 0x555555558010
지역 변수 주소: 0x7fffffffe6b4
전역 변수 주소: 0x555555558010
지역 변수 주소: 0x7fffffffe6b4

읽는 법: 두 번의 주소가 완전히 같습니다. 여기서 생각해 보세요. 이 컴퓨터에는 지금도 수십 개의 프로세스가 동시에 돌고 있고, ASLR을 끄면 그 프로세스들은 전부 이렇게 똑같은 가상 주소를 갖습니다. 그런데 RAM 칩은 하나뿐입니다. 같은 가상 주소 0x555555558010이 수십 개의 서로 다른 물리 위치로 연결되고 있는 것입니다. 이것이 오늘의 핵심 문장 "같은 가상 주소 ≠ 같은 물리 메모리"의 직접적인 증거입니다.

: 주소가 "각 프로세스의 꿈속 좌표"라는 감각을 잡는 단계입니다. 예측이 맞았든 틀렸든, 이제 그 이유까지 설명할 수 있어야 합니다.

3-3. 프로세스의 메모리 지도 들여다보기

리눅스에는 각 프로세스의 가상 주소 지도를 보여 주는 가상 파일이 있습니다.

입력

cat /proc/self/maps | head -n 6
cat /proc/self/maps | grep stack
getconf PAGESIZE

출력 (2026-09-09 실측):

5eee5da28000-5eee5da2a000 r--p 00000000 08:30 1510    /usr/bin/cat
5eee5da2a000-5eee5da2f000 r-xp 00002000 08:30 1510    /usr/bin/cat
5eee5da2f000-5eee5da31000 r--p 00007000 08:30 1510    /usr/bin/cat
5eee5da31000-5eee5da32000 r--p 00008000 08:30 1510    /usr/bin/cat
5eee5da32000-5eee5da33000 rw-p 00009000 08:30 1510    /usr/bin/cat
5eee7de77000-5eee7de98000 rw-p 00000000 00:00 0       [heap]
7ffdc9c7e000-7ffdc9c9f000 rw-p 00000000 00:00 0       [stack]
4096

읽는 법: 각 줄은 가상 주소 공간의 한 구간입니다. 처음 두 열이 시작 주소와 끝 주소, 그 다음이 권한(r: 읽기, w: 쓰기, x: 실행, p: 이 프로세스 전용)입니다. Step 60에서 배운 코드 영역(r-xp, 실행 가능), 데이터 영역, 힙([heap]), 스택([stack])이 전부 주소 순서대로 놓여 있습니다. 그리고 구간 경계를 보면 전부 …000으로 끝납니다 — getconf PAGESIZE가 알려 준 4096바이트(0x1000)의 배수이기 때문입니다. 지도의 눈금이 페이지인 것입니다.

: 그림으로만 배운 "메모리 지도"가 실제로 존재하며 운영체제가 이것을 관리한다는 것을 확인하는 단계입니다. 숫자를 self 대신 다른 PID로 바꾸면(/proc/PID/maps) 그 프로세스의 지도도 볼 수 있습니다.

3-4. 권한 문자 세기 — 지도의 색깔 분포

maps 출력의 권한 열(두 번째)만 뽑아 종류별로 세어 봅시다.

입력

cat /proc/self/maps | tr -s " " | cut -d" " -f2 | sort | uniq -c | sort -rn

출력 (2026-09-09 실측):

     23 r--p
      9 rw-p
      4 r-xp
      1 r--s

읽는 법: tr -s " "는 연속 공백을 하나로 압축하고, cut -d" " -f2는 두 번째 열(권한)만 뽑고, sort | uniq -c가 종류별 개수를 셉니다. r-xp(읽기+실행, 코드 구간), rw-p(읽기+쓰기, 데이터/힙/스택), r–p(읽기 전용)가 뚜렷이 구분되어 있습니다.

: "실행 가능한 구역과 쓰기 가능한 구역을 분리해 둔다"는 것은 단순한 정리가 아니라 보안 설계입니다. 코드 구역에 쓰기가 막혀 있어야 공격자가 기계어를 덮어쓸 수 없고, 데이터 구역에 실행이 막혀 있어야 공격자가 심어 둔 코드를 실행시킬 수 없습니다. 이 권한 문자들이 뒤에서 배울 DEP/NX라는 방어 기술의 실체입니다.

3-5. 물리 메모리와 스왑 확인

이번에는 착각의 바깥, 진짜 RAM을 봅니다.

입력

free -h

출력 (2026-09-09 실측):

               total        used        free      shared  buff/cache   available
Mem:           7.5Gi       621Mi       6.0Gi       3.6Mi       1.1Gi       6.9Gi
Swap:          2.0Gi          0B       2.0Gi

읽는 법: Mem 행이 실제 RAM(7.5GB)이고, Swap 행이 디스크에 마련된 비상용 메모리 공간(2GB)입니다. 여러 프로세스가 각자 큰 주소 공간을 "가진 것처럼" 행동해도 합계가 넘치지 않는 이유가 바로 공유와 스왑입니다.

: 가상 메모리의 약속("너희 각자 큰 공간을 가진 것처럼 행동해")이 실제 자원(RAM 7.5GB + 스왑 2GB)과 어떻게 연결되는지 감을 잡는 단계입니다. 다음 실험이 그 경계를 직접 찔러 봅니다.

3-6. 예측해 보기 — RAM보다 큰 메모리를 빌릴 수 있을까

마지막 실험입니다. 이 컴퓨터의 RAM은 7.5GB입니다. 프로그램에서 malloc으로 9GB, 10GB, 12GB를 요청하면 각각 성공할까요 실패할까요? 예측을 적고 확인해 봅니다.

입력 (big2.c)

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    long gb = atol(argv[1]);
    size_t size = (size_t)gb * 1024 * 1024 * 1024;
    char *p = malloc(size);
    if (p == NULL) printf("%ldGB 요청 -> 실패 (NULL)\n", gb);
    else printf("%ldGB 요청 -> 성공 (주소 %p)\n", gb, (void*)p);
    return 0;
}

입력

gcc -o big2 big2.c
./big2 9; ./big2 10; ./big2 12
free -h | head -n 2

출력 (2026-09-09 실측):

9GB 요청 -> 성공 (주소 0x7ff90d5ff010)
10GB 요청 -> 실패 (NULL)
12GB 요청 -> 실패 (NULL)
               total        used        free      ...
Mem:            7.5Gi       666Mi       5.8Gi      ...

읽는 법: 세 가지 발견이 있습니다. 첫째, RAM(7.5GB)보다 큰 9GB 할당이 성공했습니다 — 운영체제가 "그래, 줄게"라고 약속만 하고 실제 RAM은 한 장도 내주지 않았기 때문입니다. 이것이 지연 할당(lazy allocation)입니다. 실제 RAM은 그 메모리에 처음 값을 쓰는 순간에야 페이지 단위로 배정됩니다. 둘째, 실행 직후 free -h의 used(666Mi)는 거의 늘지 않았습니다 — 역시 약속만 오갔다는 증거입니다. 셋째, 10GB부터는 거절당했습니다. 약속도 무한정은 아니어서, 이 랩에서는 RAM과 스왑을 합한 9.5GB 언저리가 약속의 한계였습니다.

: 가상 메모리가 "약속의 경제"라는 것을 보여 주는 실험입니다. 주소 공간은 넉넉히 퍼주고, 실체(물리 페이지)는 필요해지는 순간에만 주되, 총량의 경계는 지킵니다. 예측이 빗나갔다면 그것이야말로 오늘의 수확입니다.


4. 미션과 연습문제

미션 — 가상 메모리 그림 에세이

  1. 종이나 그림 도구에 왼쪽에 "프로세스 A의 가상 주소 공간", 오른쪽에 "물리 RAM"을 그립니다
  2. 가상 공간을 코드/데이터/힙/스택으로 나누고(Step 60 복습), 각 영역에서 RAM의 임의 위치로 화살표를 그립니다. 이 화살표 다발이 페이지 테이블입니다
  3. 프로세스 B를 하나 더 그리고, A와 같은 가상 주소에서 다른 RAM 위치로 가는 화살표를 추가합니다
  4. C 라이브러리 영역만큼은 A와 B의 화살표가 같은 RAM 위치를 가리키게 그려 "공유"를 표현합니다
  5. 그림 아래에 세 문장을 적습니다. ① 같은 가상 주소가 다른 실체를 가리키는 이유 ② 이 구조가 프로세스 격리를 어떻게 만드는지 ③ 스왑은 이 그림 어디에 해당하는지

연습문제

문제 1. 가상 주소와 물리 주소의 차이를 식당의 "테이블 번호와 조리대" 비유 또는 자기만의 비유로 설명해 보세요.

문제 2. 3-2 실험에서 ASLR을 끄면 모든 프로세스가 같은 가상 주소를 갖는데도 충돌하지 않는 이유를 페이지 테이블 관점으로 설명해 보세요.

문제 3. maps 출력에서 구간 경계가 전부 …000으로 끝나는 이유를 페이지 크기와 연결해 설명해 보세요.

문제 4. 9GB malloc이 성공하고 10GB가 실패한 실측(2026-09-09)을 "약속의 경제"라는 관점으로 설명해 보세요. 성공한 9GB가 사용한 실제 RAM은 얼마인가요?


5. 모범 답안과 완료 기준

미션 모범 답안

그림의 점검 포인트와 세 문장의 예시입니다:

[그림 점검] A의 코드/데이터/힙/스택 → RAM의 서로 떨어진 위치로 화살표.
B도 같은 모양의 가상 공간에서 RAM의 다른 위치로 화살표.
A와 B의 같은 가상 주소가 RAM의 다른 칸을 가리키는가? 라이브러리 구간만
A와 B의 화살표가 RAM의 같은 칸을 가리키는가? 이 두 가지가 그려졌으면 성공.

[세 문장 예시]
① 페이지 테이블이 프로세스마다 따로 있어서, 같은 가상 주소라도 표에 적힌
   물리 페이지가 다르기 때문이다.
② B의 페이지 테이블에는 A의 물리 페이지가 아예 적혀 있지 않으므로,
   B는 어떤 가상 주소를 찔러도 A의 메모리에 닿을 수 없다 — 이것이 격리이다.
③ 스왑은 화살표의 도착지가 RAM 대신 디스크의 대기 구역인 경우이다.
   페이지 테이블에는 "지금은 디스크에 있음"이라고 표시된다.

검증하는 법: ① 그림에 같은 가상 주소 → 다른 물리 주소 화살표가 있는가. ② 공유 구간 하나가 그려졌는가. ③ 세 문장이 자기 말로 쓰였는가. 셋이 ‘예’이면 완성입니다. 이 그림이 완성되면 운영체제 수업 한 챕터를 통째로 소화한 것입니다.

연습문제 해답

문제 1 해답. 가상 주소는 프로세스가 보고 쓰는 "테이블 번호"이고, 물리 주소는 RAM 칩 위의 진짜 위치인 "조리대"입니다. 손님(프로세스)은 자기 테이블 번호만 알면 되고, 번호가 어느 조리대와 연결되는지는 식당 직원(운영체제의 페이지 테이블 + CPU의 MMU)만 압니다. 정확한 용어 병기: 가상 주소(virtual address), 물리 주소(physical address).

문제 2 해답. 페이지 테이블은 프로세스마다 따로 있기 때문입니다. 같은 가상 주소 0x555555558010이라도 프로세스 A의 표에는 RAM의 이쪽이, 프로세스 B의 표에는 RAM의 저쪽이 적혀 있습니다. 변환을 거치는 순간 서로 다른 물리 위치가 되므로 충돌하지 않습니다 (2026-09-09, setarch -R로 두 실행의 주소가 완전히 같아지는 것을 실측).

문제 3 해답. 메모리 구간이 페이지(이 랩에서는 4096바이트, 2026-09-09 getconf PAGESIZE 실측) 단위로 잘리기 때문입니다. 4096은 16진수로 0x1000이라, 페이지 경계 주소는 항상 16진수 끝 세 자리가 000이 됩니다. maps의 구간 경계가 전부 …000인 것은 지도의 눈금이 페이지라는 증거입니다.

문제 4 해답. malloc은 "주소 공간을 주겠다"는 약속을 먼저 하고, 실제 RAM(물리 페이지)은 그 메모리에 처음 값을 쓰는 순간에 배정합니다(지연 할당). 그래서 9GB를 "빌린" 직후에도 사용한 실제 RAM은 사실상 0입니다 — free -h의 used가 666Mi에 머문 것이 증거입니다. 다만 약속의 총량도 한계가 있어, RAM과 스왑을 합한 양(이 랩에서는 약 9.5GB)을 넘는 10GB 요청은 아예 거절됐습니다.

완료 기준 체크리스트

  • [ ] 가상 주소와 물리 주소의 차이를 예시와 함께 설명할 수 있다
  • [ ] 페이지 테이블의 역할을 그림으로 그릴 수 있다
  • [ ] "같은 가상 주소 ≠ 같은 물리 메모리"를 실험으로 확인했다
  • [ ] /proc/self/maps에서 코드/힙/스택 구간을 찾을 수 있다
  • [ ] 지연 할당이 무엇인지 9GB 실험으로 설명할 수 있다
  • [ ] 실행 가능 구역과 쓰기 가능 구역의 분리가 보안 설계임을 설명할 수 있다
  • [ ] 미션: 가상 메모리 그림 에세이를 완성했다

6. 흔한 실수와 해결

벽 1. 개념이 너무 추상적이에요

증상: 가상 주소니 페이지 테이블이니 하는 말이 머리에 안 들어옵니다.
원인: 정상입니다. 가상 메모리는 원래 눈에 보이지 않는 장치라 추상적일 수밖에 없습니다.
해결: 오늘 핵심 한 문장, "같은 가상 주소 ≠ 같은 물리 메모리"만 붙들고 3-2 실험(ASLR을 끄니 주소가 완전히 같아졌다)을 떠올리세요. 실험 장면이 곧 개념입니다.

벽 2. /proc/self/maps 출력이 너무 많아요

증상: 수십 줄이 쏟아져서 어디를 봐야 할지 모르겠습니다.
원인: 라이브러리 하나하나가 각자 구간을 차지하기 때문입니다.
해결: 처음엔 head로 위쪽만 보고, [heap][stack]이 적힌 줄 두 개만 찾아도 충분합니다. 나머지는 "라이브러리들이구나" 정도로 넘기세요.

벽 3. 큰 malloc이 실패해요

증상 (2026-09-09 실측): ./big2 10에서 10GB 요청 -> 실패 (NULL)이 나옵니다.
원인: 약속의 총량 한계(RAM + 스왑, 이 랩에서는 약 9.5GB)를 넘는 요청은 운영체제가 거절합니다. 시스템마다 overcommit(얼마나 초과 약속을 허용할지) 정책이 달라 한계도 다릅니다.
해결: 실패도 정상적인 결과이며 좋은 관찰입니다. 크기를 1GB씩 바꿔 가며 "내 컴퓨터는 어디까지 약속해 주는가"의 경계를 찾는 것 자체가 훌륭한 실험입니다.

벽 4. 두 실행의 주소가 완전히 달라요

증상: 3-1 실험에서 앞자리가 전혀 다릅니다 (0x5b57… → 0x5eb1…).
원인: ASLR(주소 공간 무작위 배치)이라는 보안 기능이 실행마다 지도의 시작점을 옮기기 때문입니다. Step 70에서 이 현상을 직접 관측 도구로 확인하게 됩니다.
해결: 달라도 괜찮습니다. 볼 것은 둘입니다 — 끝자리가 보존되는가(상대 위치 불변), 어느 쪽도 진짜 RAM 위치가 아니라는 점. 매번 바뀌는 현상도 관찰 일지에 기록해 두면 좋은 자료가 됩니다.

벽 5. setarch -R이 안 돼요

증상: setarch -R ./addr이 오류를 내거나 주소가 여전히 바뀝니다.
원인: 일부 환경(특정 컨테이너, 오래된 WSL 커널)에서는 ASLR 끄기가 허용되지 않습니다.
해결: 3-2 실험의 결론("같은 가상 주소 → 서로 다른 물리")은 3-1의 끝자리 보존 관찰과 maps 실험으로도 충분히 이해할 수 있습니다. 안 되는 환경에서는 그 사실을 적고 넘어가세요.


7. 정리

오늘의 개념

개념 한 줄 설명
가상 주소 / 물리 주소 프로세스가 쓰는 꿈속 좌표 / RAM 칩 위의 진짜 위치
페이지 주소 변환의 단위 조각 — 이 랩에서는 4096바이트 (2026-09-09 실측)
페이지 테이블 가상 → 물리 대조표 — 프로세스마다 따로 있다
MMU 변환을 하드웨어 속도로 해 주는 CPU 안의 장치
스왑(swap) 당분간 안 쓰는 페이지의 디스크 대기 구역
지연 할당 malloc은 약속을 먼저, 실제 RAM은 쓰는 순간에 배정
ASLR 실행마다 지도의 시작점을 옮기는 무작위화 — 끝자리는 보존된다

오늘의 명령어

명령어 하는 일
cat /proc/self/maps 이 프로세스의 가상 주소 지도 보기
cat /proc/PID/maps 다른 프로세스의 지도 보기
free -h 물리 메모리와 스왑의 현황
getconf PAGESIZE 페이지 크기 확인
setarch -R ./프로그램 ASLR을 끄고 한 프로세스만 실행
tr -s " " | cut -d" " -f2 | sort | uniq -c 열 뽑아 종류별로 세기

명령어보다 중요한 감각

오늘로써 여러분은 "주소"라는 단어를 두 겹으로 보게 됐습니다. 포인터 속 숫자는 세계의 진짜 좌표가 아니라, 각 프로세스의 꿈속 좌표였습니다. 이 꿈과 현실을 잇는 다리(페이지 테이블)를 아는 사람만이 메모리 관련 공격과 방어를 진짜로 이해할 수 있습니다. 기억할 문장은 하나입니다 — "같은 가상 주소 ≠ 같은 물리 메모리."

보안과의 연결: 가상 메모리는 프로세스마다 독립된 세계를 주는 보안 성벽입니다. 그런데 이 성벽을 "합법적으로" 넘는 문도 있습니다 — 디버거(gdb)가 다른 프로세스의 메모리를 읽고 쓸 수 있는 것은 운영체제가 ptrace라는 특별한 문을 허가된 프로세스에게만 열어 주기 때문입니다. 반대로 공격자는 페이지 테이블 설정 미스나 커널 취약점으로 이 성벽을 넘으려 합니다. 오늘 실험한 모든 메모리 관찰은 여러분 자신의 프로세스 안에서만 일어났고, 모든 공격 실습은 내 랩·합법 플랫폼에서만입니다. 실서비스 무단 공격은 범죄입니다.


전부 체크되면 Step 69 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.