Huge Page는 TLB 미스를 줄여 성능을 비약적으로 향상시키지만, 리눅스 커널은 이를 사용하는 여러 가지 통로를 제공합니다. 모든 API를 외울 필요는 없습니다. 실무에서 가장 많이 쓰이는 순서대로 전략을 세워봅시다.

1. Huge Page 사용 방식 인기 투표

실제 오픈소스 프로젝트와 기업용 솔루션에서 채택하는 빈도를 기준으로 나열한 우선순위입니다.

  1. 1위: madvise (THP) - 가장 유연하고 대중적입니다. (Redis, JVM 등)
  2. 2위: Shared Memory (shmget / hugetlbfs) - DB 엔진의 표준입니다. (PostgreSQL, MySQL 등)
  3. 3위: mmap (+ MAP_HUGETLB) - 엄격한 제어가 필요한 특수 분야에서 씁니다. (DPDK 등)
  4. 4위: memfd_create - 최신 리눅스 환경에서 선호되는 깔끔한 방식입니다.
  5. 5위: libhugetlbfs - 코드를 고칠 수 없는 레거시 환경용입니다.

2. madvise 방식: “가장 우아한 힌트”

이 방식은 Transparent Huge Pages (THP) 기능을 활용합니다. 커널이 평소에는 4KB 페이지로 관리하다가, 개발자가 “여기 좀 합쳐줘"라고 힌트를 주면 Huge Page로 전환합니다.

특징

  • Best-effort: Huge Page 할당에 실패해도 프로그램이 죽지 않고 4KB 페이지로 계속 돌아갑니다.
  • 범용성: 가장 안전하며, 대부분의 시나리오에서 첫 번째로 고려해야 할 방식입니다.

예제 코드

#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>

int main() {
    size_t size = 100 * 1024 * 1024; // 100MB

    // 1. Huge Page 크기(보통 2MB)의 배수로 메모리를 정렬하여 할당합니다.
    void *ptr;
    if (posix_memalign(&ptr, 2 * 1024 * 1024, size) != 0) {
        return 1;
    }

    // 2. 커널에 이 영역을 Huge Page로 관리해달라고 힌트를 줍니다.
    if (madvise(ptr, size, MADV_HUGEPAGE) == 0) {
        printf("성공: 커널이 이 영역을 Huge Page로 관리하려고 시도합니다.\n");
    }

    // ... 비즈니스 로직 ...

    free(ptr);
    return 0;
}

3. Shared Memory (shm) 방식: “데이터베이스의 표준”

MySQL이나 PostgreSQL 같은 DB 엔진들이 거대한 버퍼 캐시를 관리할 때 쓰는 방식입니다.

왜 DB에서 쓰는가?

  1. 공유(Shared): 여러 프로세스가 동시에 같은 데이터 영역을 바라봐야 합니다.
  2. 영속성(Persistence): DB 엔진이 크래시가 나더라도, 공유 메모리에 있는 데이터는 유지되도록 설정할 수 있어 복구 속도가 빠릅니다.

예제 코드 (System V API)

#include <sys/shm.h>
#include <stdio.h>

int main() {
    // SHM_HUGETLB 플래그를 사용하여 Huge Page 영역에 할당을 요청합니다.
    int shmid = shmget(IPC_PRIVATE, 256 * 1024 * 1024, SHM_HUGETLB | IPC_CREAT | 0600);

    if (shmid < 0) {
        perror("Huge Page 예약이 안 되어 있거나 할당에 실패했습니다.");
        return 1;
    }

    void *ptr = shmat(shmid, NULL, 0);
    // ... 공유 버퍼로 사용 ...

    return 0;
}

4. 그냥 mmap (MAP_HUGETLB): “확실한 격리”

이론적으로 가장 먼저 배우는 방식이지만, 실제로는 생각보다 인기가 없습니다.

왜 인기가 없을까? (단점)

  • 엄격함 (Fail-fast): 커널에 미리 예약된 Huge Page가 단 하나라도 부족하면 mmap 호출 자체가 즉시 실패(ENOMEM)합니다. 프로그램 실행이 안 될 수 있다는 리스크가 큽니다.
  • 사전 설정: 관리자가 미리 /proc/sys/vm/nr_hugepages 값을 설정해둬야 합니다.

그럼에도 필요한 이유 (보안과 격리)

  • 보안(Security): shm은 시스템 전역 자원이라 키를 알면 누구나 접근 가능하지만, 익명 mmap은 해당 프로세스와 그 자식들만의 비밀 공간입니다.
  • 깔끔한 정리: 프로세스가 죽으면 커널이 즉시 메모리를 회수합니다. shm처럼 ‘좀비 메모리’가 남을 걱정이 없습니다.

5. 마무리: 상황별 선택 가이드

선택 기준 구현 방식 난이도 특징 / 추천 시나리오
“성능은 올리고 싶지만 위험은 싫다” madvise ⭐ (매우 쉬움) 안전함, 실패 시 Fallback 제공 / 일반적인 성능 향상
“프로세스가 죽어도 데이터는 남아야 한다” shmget / hugetlbfs ⭐⭐⭐ (설정 복잡) 프로세스 간 공유 / DB 엔진, 다중 프로세스 간 대규모 공유
“내 프로세스만 쓰는 고정 성능이 필요하다” mmap (MAP_HUGETLB) ⭐⭐⭐ (엄격함) 보안 강화, 엄격한 할당 체크 / 고성능 패킷 처리, 실시간 시스템
“파일 시스템 없이 FD로 공유하고 싶다” memfd_create ⭐⭐ (깔끔함) 현대적인 FD 기반 인터페이스 / 가상화(VM), 샌드박스 보안 통신
“코드 수정이 불가능한 레거시” libhugetlbfs ⭐ (설정만 필요) LD_PRELOAD 활용, 라이브러리 래핑 / 소스 코드 수정이 불가능한 레거시 앱

맺음말

프로그래머에게 Huge Page는 단순히 “빠른 메모리"가 아닙니다. **“내가 커널의 메모리 관리 방식을 얼마나 엄격하게 통제할 것인가”**를 결정하는 설계의 문제입니다.

처음 시작한다면 **madvise**로 안전하게 시작하고, 데이터베이스와 같은 인프라 성격의 소프트웨어를 개발한다면 **shm**이나 **hugetlbfs**로 눈을 돌려보시기 바랍니다.