Ryotta's Linux 7.0 MM

메모리 관리 서브시스템 완전 분석

# 34. memfd — 메모리 기반 파일 디스크립터

개요 (Overview)

memfd는 tmpfs 또는 hugetlbfs 위에 익명 메모리 파일을 생성하는 시스템 호출이다. memfd_create()로 생성된 파일 디스크립터는 unnamed memory object로, 파일 시스템에 마운트되지 않지만 일반 파일처럼 mmap(), read(), write()가 가능하다. 주요 사용 사례로는 IPC, 세이프 샌드박스, SECCOMP 필터와의 조합 등이 있다.

memfd의 핵심 기능은 파일 봉인(File Sealing)이다. 여러 프로세스가 익명 메모리를 공유할 때, 상대방이 수행할 수 있는 연산을 제한하는 메커니즘이다. 봉인은 추가만 가능하고 제거할 수 없으므로, 서로 신뢰하지 않는 프로세스 사이에서도 안전한 메모리 공유가 가능하다.

호출 흐름:

memfd_create 호출 흐름

자료구조 관계:

memfd 자료구조 관계도

소스 파일 경로:

mm/memfd.c                          ← memfd_create 시스템 호출, 봉인 관리
include/linux/memfd.h               ← memfd API 선언
include/linux/shmem_fs.h            ← shmem_inode_info (봉인 저장)
include/linux/hugetlb.h             ← hugetlbfs_inode_info (봉인 저장)
include/uapi/linux/memfd.h          ← MFD_* 플래그 정의
include/uapi/linux/fcntl.h          ← F_SEAL_* 플래그 정의

빠른 점검 명령

# memfd 파일 디스크립터 확인
ls -la /proc/<pid>/fd | grep memfd

# memfd 파일 정보 확인
cat /proc/<pid>/fdinfo/<fd>

# 시스템 전체 memfd 카운트 확인
cat /proc/sys/fs/memfd_noexec       # noexec 범위 확인

# tmpfs 마운트 확인 (memfd의 기본 백엔드)
mount | grep tmpfs

# HugeTLBFS 마운트 확인 (MFD_HUGETLB용)
mount | grep hugetlb

# memfd 시스템 호출 호출 수 확인
cat /proc/kallsyms | grep sys_memfd_create

# 현재 프로세스의 memfd 열기
python3 -c "import os; fd = os.memfd_create('test', 0); print(fd); input()"

# memfd 봉인 상태 확인 (Python)
python3 -c "
import fcntl, os
fd = os.memfd_create('test', os.MFD_ALLOW_SEALING)
print('seals:', fcntl.fcntl(fd, fcntl.F_GET_SEALS))
"

핵심 자료구조

shmem_inode_info — tmpfs inode 메타데이터 (봉인 저장)

// include/linux/shmem_fs.h:36-59
struct shmem_inode_info {
    spinlock_t          lock;
    unsigned int        seals;      // memfd 봉인 비트마스크 저장
    unsigned long       flags;
    unsigned long       alloced;    // 파일에 할당된 데이터 페이지 수
    unsigned long       swapped;    // swap에 할당된 하위 합계
    union {
        struct offset_ctx dir_offsets;
        struct {
            struct list_head shrinklist;
            struct list_head swaplist;
        };
    };
    struct timespec64   i_crtime;   // 파일 생성 시간
    struct shared_policy policy;    // NUMA 메모리 정책
    struct simple_xattrs xattrs;
    pgoff_t             fallocend;
    unsigned int        fsflags;
    atomic_t            stop_eviction;
    struct inode        vfs_inode;
};

hugetlbfs_inode_info — HugeTLB inode 메타데이터

// include/linux/hugetlb.h:519-522
struct hugetlbfs_inode_info {
    struct inode vfs_inode;
    unsigned int seals;  // memfd 봉인 비트마스크 (shmem과 동일 형식)
};

F_SEAL_* 플래그 — 파일 봉인 유형

// include/uapi/linux/fcntl.h:47-52
#define F_SEAL_SEAL          0x0001  // 추가 봉인 방지
#define F_SEAL_SHRINK        0x0002  // 파일 축소 방지
#define F_SEAL_GROW          0x0004  // 파일 확장 방지
#define F_SEAL_WRITE         0x0008  // 쓰기 방지
#define F_SEAL_FUTURE_WRITE  0x0010  // 매핑된 상태에서 향후 쓰기 방지
#define F_SEAL_EXEC          0x0020  // exec 비트 변경 방지

MFD_* 플래그 — memfd_create 옵션

// include/uapi/linux/memfd.h:8-14
#define MFD_CLOEXEC          0x0001  // exec 시 닫기
#define MFD_ALLOW_SEALING    0x0002  // 봉인 허용
#define MFD_HUGETLB          0x0004  // HugeTLB 백엔드 사용
#define MFD_NOEXEC_SEAL      0x0008  // 실행 불가 + 봉인
#define MFD_EXEC             0x0010  // 실행 가능

MEMFD_TAG_PINNED — XArray 태그

// mm/memfd.c:29-30
#define MEMFD_TAG_PINNED    PAGECACHE_TAG_TOWRITE  // 추가 참조가 있는 folio 태그
#define LAST_SCAN           4                       // 최대 스캔 횟수 (~150ms)

핵심 함수

1. SYSCALL_DEFINE2(memfd_create) — 시스템 호출 진입점

// mm/memfd.c:505-523
SYSCALL_DEFINE2(memfd_create,
        const char __user *, uname,
        unsigned int, flags)
{
    char *name __free(kfree) = NULL;
    unsigned int fd_flags;
    int error;

    error = sanitize_flags(&flags);     // 플래그 유효성 검증
    if (error < 0)
        return error;

    name = alloc_name(uname);           // "memfd:" 접두사 추가
    if (IS_ERR(name))
        return PTR_ERR(name);

    fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0;
    return FD_ADD(fd_flags, memfd_alloc_file(name, flags));  // fd 반환
}
  • 역할: 사용자空间에서 memfd_create("name", flags) 호출 시 진입
  • 분기: MFD_CLOEXECO_CLOEXEC 설정, MFD_HUGETLB → hugetlbfs 백엔드 선택
  • 2. memfd_alloc_file — 파일 생성

    // mm/memfd.c:458-503
    struct file *memfd_alloc_file(const char *name, unsigned int flags)
    {
        // MFD_HUGETLB이면 hugetlb_file_setup, 아니면 shmem_file_setup
        if (flags & MFD_HUGETLB) {
            file = hugetlb_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT),
                        HUGETLB_ANONHUGE_INODE,
                        (flags >> MFD_HUGE_SHIFT) & MFD_HUGE_MASK);
        } else {
            file = shmem_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT));
        }
        // 보안 초기화, 파일 모드 설정 (FMODE_LSEEK, FMODE_PREAD 등)
        // MFD_NOEXEC_SEAL → exec 비트 제거 + F_SEAL_EXEC 설정
        // MFD_ALLOW_SEALING → F_SEAL_SEAL 해제 (봉인 허용)
    }
  • 역할: 백엔드 파일 시스템(tmpfs/hugetlbfs)에 익명 파일 생성
  • 분기: MFD_HUGETLB → HugeTLB 할당, MFD_NOEXEC_SEAL → exec 비트 제거
  • 3. memfd_add_seals — 파일 봉인 추가

    // mm/memfd.c:230-310
    static int memfd_add_seals(struct file *file, unsigned int seals)
    {
        // 쓰기 권한 확인
        if (!(file->f_mode & FMODE_WRITE))
            return -EPERM;
    
        // 기존 SEAL_SEAL이 설정되어 있으면 추가 봉인 불가
        if (*file_seals & F_SEAL_SEAL)
            return -EPERM;
    
        // SEAL_WRITE 추가 시: 매핑 거부 → pinned folio 대기
        if ((seals & F_SEAL_WRITE) && !(*file_seals & F_SEAL_WRITE)) {
            error = mapping_deny_writable(file->f_mapping);
            error = memfd_wait_for_pins(file->f_mapping);
        }
    
        // SEAL_EXEC: exec 비트가 있으면 SHRINK/GROW/WRITE/FUTURE_WRITE도 추가
        if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
            seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
    }
  • 역할: 파일에 봉인 비트 추가 (제거 불가)
  • 분기: F_SEAL_WRITEmapping_deny_writable + memfd_wait_for_pins, F_SEAL_EXEC → W^X 정책 자동 적용
  • 4. memfd_wait_for_pins — pinned folio 대기

    // mm/memfd.c:155-208
    static int memfd_wait_for_pins(struct address_space *mapping)
    {
        // 1단계: 모든 folio 스캔하여 extra ref가 있는 것 태그
        memfd_tag_pins(&xas);
    
        // 2단계: 최대 4회 스캔 (지수 백오프)
        for (scan = 0; scan <= LAST_SCAN; scan++) {
            if (!scan)
                lru_add_drain_all();        // 첫 스캔: LRU 플러시
            else
                schedule_timeout_killable((HZ << scan) / 200);  // 백오프 대기
    
            // MEMFD_TAG_PINNED 태그가 남아있으면 계속 대기
            // 마지막 스캔에서 still pinned → -EBUSY 반환
        }
    }
  • 역할: GUP(direct I/O, AIO 등)로 pin된 folio가 해제될 때까지 대기
  • 분기: 태그 없음 → 즉시 반환, 백오프 스캔 후仍有 → -EBUSY
  • 5. memfd_check_seals_mmap — mmap 시 봉인 검증

    // mm/memfd.c:397-407
    int memfd_check_seals_mmap(struct file *file, vm_flags_t *vm_flags_ptr)
    {
        unsigned int seals = *memfd_file_seals_ptr(file);
    
        if (is_write_sealed(seals))
            err = check_write_seal(vm_flags_ptr);  // 쓰기 매핑 차단
        return err;
    }
  • 역할: mmap() 호출 시 write 봉인 여부 확인
  • 분기: VM_SHARED | VM_WRITE-EPERM, 읽기 전용 → VM_MAYWRITE 제거
  • 호출 흐름

    memfd_create("test", MFD_CLOEXEC | MFD_ALLOW_SEALING)
      │
      ├─ sanitize_flags()          ← MFD_* 플래그 유효성 검증
      ├─ alloc_name()              ← "memfd:test" 이름 생성
      └─ memfd_alloc_file()
           ├─ shmem_file_setup()   ← tmpfs 익명 파일 생성 (기본)
           │   또는
           ├─ hugetlb_file_setup() ← HugeTLB 파일 생성 (MFD_HUGETLB)
           ├─ security_inode_init_security_anon()
           └─ 파일 모드 설정 (FMODE_LSEEK 등)
    
    fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE)
      │
      └─ memfd_add_seals()
           ├─ mapping_deny_writable()      ← 쓰기 매핑 거부
           └─ memfd_wait_for_pins()
                ├─ memfd_tag_pins()        ← pinned folio 태깅
                └─ 스캔 + 백오프 대기 (최대 ~150ms)
    
    mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)
      │
      └─ memfd_check_seals_mmap()
           └─ check_write_seal()           ← write 봉인 시 -EPERM

    조건별 비교

    구분tmpfs 백엔드 (기본)hugetlbfs 백엔드 (MFD_HUGETLB)
    **생성 함수**`shmem_file_setup()``hugetlb_file_setup()`
    **할당 시점**demand paging (폴트 시)즉시 예약 (fallocate)
    **커널 메모리 사용**실제 사용량만사전 할당 (고정)
    **성능**일반 메모리 수준고성능 DMA 가능
    **Swappable**가능불가능
    **봉인 구조체**`shmem_inode_info.seals``hugetlbfs_inode_info.seals`
    봉인 플래그효과SEAL_EXEC 시 자동 추가
    `F_SEAL_SEAL`추가 봉인 방지-
    `F_SEAL_SHRINK`파일 축소 방지
    `F_SEAL_GROW`파일 확장 방지
    `F_SEAL_WRITE`현재 쓰기 방지
    `F_SEAL_FUTURE_WRITE`향후 쓰기 방지
    `F_SEAL_EXEC`exec 비트 변경 방지-
    noexec 범위 (sysctl)동작
    `MEMFD_NOEXEC_SCOPE_EXEC` (0)`MFD_EXEC` 기본, noexec 없음
    `MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL` (1)`MFD_NOEXEC_SEAL` 자동 적용
    `MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED` (2)`MFD_NOEXEC_SEAL` 필수, 없으면 `-EACCES`

    관련 문서

  • 16-shmem.html — tmpfs/shmem 백엔드 상세
  • 43-hugetlb.html — HugeTLBFS 백엔드 상세
  • 25-mseal.html — VMA 봉인 (mseal)과 비교
  • 00-overview.html — 메모리 관리 개요