이 코드는 어떤 문제를 푸나요?
부팅 직후에는 일반 메모리 할당기가 아직 없습니다. 그렇지만 페이지 테이블과 커널 자료구조를 만들 RAM은 필요합니다. memblock은 RAM 구간과 예약 구간을 따로 기록하여, 이미 커널 이미지나 펌웨어가 쓰는 주소를 다시 나누어 주지 않도록 합니다. 여기서는 예약 정보를 등록하는 짧은 함수 전체를 읽습니다.
읽을 범위: v6.18.37 · mm/memblock.c · __memblock_reserve 928–937행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
물리 주소 구간
base는 첫 바이트의 물리 주소이고 size는 바이트 수입니다. 관리 대상은 보통 [base, base + size)로 표현합니다. 끝 주소를 포함하는 로그를 찍을 때만 base + size - 1을 사용합니다.
memory와 reserved
memory는 파악한 메모리 구간이고 reserved는 재할당하면 안 되는 구간입니다. 예약은 RAM 내용을 복사하거나 지우는 명령이 아니라 할당기가 사용할 수 있는 범위를 줄이는 기록입니다.
__init_memblock
memblock 초기화 코드의 수명에 관한 표기입니다. 관련 설정에 따라 초기화 이후에도 유지할 수 있으므로 항상 즉시 폐기된다고 읽으시면 안 됩니다.
처음 읽을 때
가상의 RAM [0x1000, 0x9000)에서 [0x3000, 0x5000)을 예약해 보세요. 남는 구간은 두 개가 되지만 전체 RAM의 물리 배치가 바뀌지는 않습니다.
더 깊이 살펴볼 때
예약 범위가 겹칠 때 memblock_add_range가 같은 영역을 중복 계산하지 않는지, nid와 flags가 다른 인접 영역을 언제 병합할 수 있는지까지 이어서 읽어 보세요.
그림으로 보는 변화

1. RAM 구간 파악
예시 memory = [0x1000, 0x9000)
화살표는 RAM 목록을 받아 예약 판단으로 넘어가는 순서입니다. 숫자는 설명용 주소입니다.
2. 일부 구간 예약
예시 reserved = [0x3000, 0x5000)
RAM을 이동하지 않습니다. 이 범위를 할당 후보에서 제외하도록 목록에 기록합니다.
3. 남는 구간 사용
예시 후보 = [0x1000, 0x3000), [0x5000, 0x9000)
화살표는 예약 정보를 반영한 결과입니다. 예약 함수 자체가 이 두 구간을 할당한다는 뜻은 아닙니다.
__memblock_reserve를 한 줄씩 읽기
줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
int __init_memblock __memblock_reserve(phys_addr_t base, phys_addr_t size,
int nid, enum memblock_flags flags)
{
phys_addr_t end = base + size - 1;
memblock_dbg("%s: [%pa-%pa] nid=%d flags=%x %pS\n", __func__,
&base, &end, nid, flags, (void *)_RET_IP_);
return memblock_add_range(&memblock.reserved, base, size, nid, flags);
}int __init_memblock __memblock_reserve(phys_addr_t base, phys_addr_t size,base부터 size바이트를 예약 목록에 넣는 함수입니다. phys_addr_t는 물리 주소를 담으며, 반환값은 등록 성공 여부를 나타냅니다.
int nid, enum memblock_flags flags)nid는 NUMA 노드 정보이고 flags는 이 구간의 속성입니다. 주소만 기록하는 것이 아니라 이후 탐색과 병합에 필요한 정보도 함께 받습니다.
phys_addr_t end = base + size - 1;로그에서 마지막 바이트까지 보여 주려고 끝 주소를 계산합니다. 관리 범위를 반열린 구간으로 쓰면 base + size가 끝 경계입니다.
memblock_dbg("%s: [%pa-%pa] nid=%d flags=%x %pS\n", __func__,memblock 디버그 출력에 함수 이름과 예약 범위를 남깁니다. %pa는 물리 주소용 형식이며 주소값을 담은 변수의 포인터를 받습니다.
&base, &end, nid, flags, (void *)_RET_IP_);시작·끝 주소, 노드, 속성, 호출 지점을 넘깁니다. _RET_IP_는 어느 초기화 코드가 이 RAM을 예약했는지 추적하는 데 사용됩니다.
return memblock_add_range(&memblock.reserved, base, size, nid, flags);memory 목록이 아니라 reserved 목록에 구간을 추가합니다. 겹침 처리와 필요한 목록 확장은 memblock_add_range가 담당하며 그 결과를 그대로 반환합니다.
함께 생각해 볼 질문
예약하면 그 주소의 데이터가 초기화되나요?
아닙니다. 이 함수는 reserved 목록에 범위를 추가합니다. 메모리를 0으로 채우는 동작은 별도입니다.
로그의 end가 base + size - 1인 이유는 무엇인가요?
마지막으로 포함되는 바이트 주소를 출력하기 때문입니다. 내부에 넘기는 인자는 여전히 base와 size입니다.
RAM이 충분한데 초기 할당이 실패할 수도 있나요?
가능합니다. 예약 구간, 정렬, 허용 주소 범위 등의 조건 때문에 요청을 만족하는 연속 구간이 없을 수 있습니다.
출처와 읽은 범위
Linux stable v6.18.37 · mm/memblock.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
