Ryotta's Linux 7.0 MM

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

Folio / Page Cache

관련 소스: mm/filemap.c, mm/folio-compat.c, mm/page-writeback.c
관련 문서: 메모리 관리 개요 · Buddy Allocator · 페이지 회수 · VMA / mmap · tmpfs / shmem

개요 (Overview)

Folio는 Linux 5.16에서 도입된 메모리 관리의 핵심 단위로, 기존 struct page의 단점을 보완합니다. 기존 페이지 캐시는 개별 페이지 단위로 관리되어 large folio(THP 등) 지원에 비효율적이었으나, folio는 compound page 전체를 하나의 단위로 관리하여 여러 페이지를 한 번에 처리할 수 있습니다. 페이지 캐시(Page Cache)는 파일 시스템의 블록 데이터를 메모리에 캐싱하는 데 사용되며, address_space 구조체를 통해 관리됩니다.

folio의 핵심 장점은 compound page의 head page만 추적하면 되어 lock 관리, dirty/writeback 추적, reference counting이 단순해진다는 점입니다. 또한 기존 page API와의 호환성을 위해 folio-compat.c에 레거시 래퍼 함수가 제공됩니다. Linux 7.0에서는 _mm_id 기반 rmap 최적화, _deferred_list를 통한 대기 분할(split under pressure), HugeTLB 전용 필드 등이 추가되어 구조적으로 더 정교해졌습니다.

소스 파일 경로:
  mm/filemap.c          — 페이지 캐시 핵심 연산 (조회, 추가, 읽기, 쓰기, 폴트)
  mm/folio-compat.c     — 기존 page API → folio 래퍼 (unlock_page, mark_page_accessed 등)
  mm/page-writeback.c   — 더티 페이지 writeback 및 dirty throttling
  include/linux/mm_types.h  — struct folio 정의 (Linux 7.0: _mm_id, _deferred_list 포함)
  include/linux/fs.h        — struct address_space, address_space_operations 정의
  include/linux/pagemap.h   — folio_lock, folio_trylock 등 잠금 API

빠른 점검 명령

# 1. 페이지 캐시에 캐싱된 페이지 수 확인
cat /proc/meminfo | grep -i "Cached"

# 2. 현재 시스템의 Active/Inactive(file) 페이지 수 확인
cat /proc/meminfo | grep -E "Active\(file\)|Inactive\(file\)"

# 3. writeback 중인 페이지 수 확인
cat /proc/meminfo | grep -i "Dirty\|Writeback"

# 4. 페이지 캐시 관련 slab 캐시 확인 (radix_tree_node → xarray)
slabtop -o | grep -i "xarray\|inode_cache\|dentry"

# 5. vm.dirty_ratio / vm.dirty_background_ratio 파라미터 확인
sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_writeback_interval vm.dirty_expire_interval

# 6. 특정 inode의 페이지 캐시 크기 확인 (debugfs)
cat /proc/<pid>/smaps | grep -A 5 "file" | grep -i "size"

# 7. 파일 캐시 히트율 확인 (perf 또는 sar)
sar -B 1 5  # pgscank/s (page reclaim), pgscand/s (direct reclaim) 관찰

# 8. 파일 시스템별 페이지 캐시 통계 확인
cat /proc/meminfo | grep -E "Cached|Buffers|SwapCached"

# 9. folio lock 대기 공정성 확인
sysctl vm.page_lock_unfairness  # 기본값: 5

# 10. writeback 스레드 동작 확인
cat /proc/vmstat | grep -E "nr_dirty|nr_writeback|nr_congested"

# 11. mmap된 파일 페이지 폴트 통계
cat /proc/vmstat | grep -E "pgmajfault|pgfault"

# 12. 주요 프로세스의 페이지 캐시 사용량 확인
cat /proc/<pid>/smaps_rollup | grep -E "RSS|PSS"

핵심 자료구조

struct folio

folio는 하나 이상의 연속된 물리 페이지를 하나의 논리적 단위로 관리합니다. struct page와의 호환성을 위해 union으로 겹쳐져 있으며, Linux 7.0에서는 _mm_id 기반 rmap 최적화, _deferred_list 등 새로운 필드가 추가되었습니다.

// include/linux/mm_types.h:401-506 (Linux 7.0)
struct folio {
    union {
        struct {
            memdesc_flags_t flags;           // Atomic 플래그 (locked, uptodate, dirty 등)
            union {
                struct list_head lru;        // LRU 리스트 (active/inactive)
                struct { void *__filler; unsigned long mlock_count; }; // Unevictable
                struct dev_pagemap *pgmap;   // ZONE_DEVICE용
            };
            struct address_space *mapping;   // 소유 address_space 포인터
            union {
                pgoff_t index;               // 파일 내 오프셋 (페이지 인덱스)
                unsigned long share;         // fsdax용 share count
            };
            union {
                void *private;               // 파일 시스템 전용 데이터 (buffer_head 등)
                swp_entry_t swap;            // swap 캐시에서의 swap entry
            };
            atomic_t _mapcount;              // RMAP에서의 매핑 카운트 (-1 = 매핑 안됨)
            atomic_t _refcount;              // 참조 카운트
            unsigned long memcg_data;        // memcgroup 데이터
        };
        struct page page;  // 기존 struct page와 호환성
    };
    // 2번째 struct page 영역 (large folio용)
    union {
        struct {
            atomic_t _large_mapcount;        // large folio 전체 매핑 카운트
            atomic_t _nr_pages_mapped;       // 실제로 매핑된 페이지 수
            atomic_t _entire_mapcount;       // 전체 매핑 카운트 (64bit)
            atomic_t _pincount;              // DMA 핀 카운트
            mm_id_mapcount_t _mm_id_mapcount[2]; // rmap용 MM ID별 매핑 카운트
            union {
                mm_id_t _mm_id[2];           // rmap용 MM ID
                unsigned long _mm_ids;       // 비트 플래그 (잠금 포함)
            };
            unsigned int _nr_pages;          // folio 내 총 페이지 수
        };
        struct page __page_1;
    };
    // 3번째 struct page 영역 (deferred split용)
    union {
        struct {
            struct list_head _deferred_list; // 메모리 압박 시 분할 대기 folio 리스트
        };
        struct page __page_2;
    };
    // 4번째 struct page 영역 (HugeTLB 전용)
    union {
        struct {
            void *_hugetlb_subpool;          // HugeTLB 서브풀
            void *_hugetlb_cgroup;           // HugeTLB memcg
            void *_hugetlb_cgroup_rsvd;      // HugeTLB memcg 예약
            void *_hugetlb_hwpoison;         // HugeTLB 하드웨어 오류
        };
        struct page __page_3;
    };
};

Linux 7.0 FOLIO_MATCH 정적 검증 (mm_types.h:508-547):

// struct folio와 struct page의 필드 오프셋이 일치하는지 컴파일 타임 검증
FOLIO_MATCH(flags, flags);           // folio->flags == page->flags
FOLIO_MATCH(lru, lru);               // folio->lru == page->lru (compound_head)
FOLIO_MATCH(mapping, mapping);       // folio->mapping == page->mapping
FOLIO_MATCH(__folio_index, index);   // folio->index == page->__folio_index
FOLIO_MATCH(_mapcount, _mapcount);   // folio->_mapcount == page->_mapcount
FOLIO_MATCH(_refcount, _refcount);   // folio->_refcount == page->_refcount

핵심 필드 설명:

필드역할
`flags`페이지 상태 플래그 (locked, uptodate, dirty, writeback, lru 등). `include/linux/page-flags.h`에서 `PG_locked`, `PG_dirty`, `PG_uptodate` 등 정의
`mapping`이 folio가 속한 `address_space` 포인터. `NULL`이면 unlinked. LSB가 1이면 anonymous page (`FOLIO_MAPPING_ANON`)
`index`파일 내 페이지 인덱스 (page offset). `address_space->i_pages` xarray에서의 키
`private`파일 시스템 전용 데이터. buffer_head 포인터, swap entry 등으로 사용
`_mapcount`이 페이지가 몇 개의 PTE에 매핑되어 있는지 추적. `-1`이면 매핑 없음
`_refcount`이 folio에 대한 참조 카운트. 0이 되면 free 가능
`_large_mapcount`large folio에서 전체 매핑 카운트 (2번째 page 영역)
`_nr_pages_mapped`large folio에서 실제로 매핑된 페이지 수
`_entire_mapcount`large folio 전체 매핑 카운트 (64bit 시스템)
`_mm_id`Linux 7.0 rmap 최적화: MM ID 기반 매핑 추적. 기존 anon_vma 체인 순회 대비 O(1) 접근 가능
`_deferred_list`메모리 압박 시 large folio를 split하기 위해 대기하는 리스트
`_nr_pages`folio가 포함하는 물리 페이지 수 (large folio)
`_hugetlb_*`HugeTLB 전용 필드: 서브풀, memcg, 하드웨어 오류 추적

struct address_space

파일 캐시의 루트 구조체입니다. 모든 캐싱된 페이지는 이 구조체의 i_pages xarray에 저장됩니다.

// include/linux/fs.h:470-490
struct address_space {
    struct inode            *host;           // 소유 inode (파일 또는 block device)
    struct xarray           i_pages;         // 페이지 캐시 (XArray 기반)
    struct rw_semaphore     invalidate_lock; // invalidate 시 coherency 보장
    gfp_t                   gfp_mask;        // 할당 시 사용할 GFP 플래그
    atomic_t                i_mmap_writable; // VM_SHARED writable 매핑 수
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
    atomic_t                nr_thps;         // non-shmem THP 수
#endif
    struct rb_root_cached   i_mmap;          // mmap된 VMA 트리 (private/shared)
    unsigned long           nrpages;         // 총 캐싱된 페이지 수
    pgoff_t                 writeback_index; // writeback 시작 오프셋
    const struct address_space_operations *a_ops;  // 파일 시스템 연산 테이블
    unsigned long           flags;           // AS_EIO, AS_ENOSPC, AS_DIRTY 등
    errseq_t                wb_err;          // 최근 writeback 에러
    spinlock_t              i_private_lock;  // i_private_list 보호
    struct list_head        i_private_list;  // 파일 시스템 전용 리스트
    struct rw_semaphore     i_mmap_rwsem;    // i_mmap 보호
    void                   *i_private_data;  // 파일 시스템 전용 데이터
} __attribute__((aligned(sizeof(long))));

struct address_space_operations

파일 시스템이 구현해야 할 페이지 캐시 연산 테이블입니다.

// include/linux/fs.h:403-444
struct address_space_operations {
    int (*read_folio)(struct file *, struct folio *);           // 단일 folio 읽기
    int (*writepages)(struct address_space *, struct writeback_control *); // 더티 페이지 writeback
    bool (*dirty_folio)(struct address_space *, struct folio *); // folio를 dirty로 표시
    void (*readahead)(struct readahead_control *);              // readahead 연산
    int (*write_begin)(const struct kiocb *, struct address_space *,
                       loff_t pos, unsigned len,
                       struct folio **foliop, void **fsdata);  // 쓰기 시작
    int (*write_end)(const struct kiocb *, struct address_space *,
                     loff_t pos, unsigned len, unsigned copied,
                     struct folio *folio, void *fsdata);       // 쓰기 완료
    bool (*release_folio)(struct folio *, gfp_t);              // folio 해제 검사
    void (*free_folio)(struct folio *folio);                   // folio 완전 해제
    // ... swap, direct_IO, migrate 등
};

핵심 함수

1. `__filemap_get_folio_mpol` — 페이지 캐시 조회/생성

// mm/filemap.c:1940-2057
struct folio *__filemap_get_folio_mpol(struct address_space *mapping,
        pgoff_t index, fgf_t fgp_flags, gfp_t gfp, struct mempolicy *policy)

역할: 지정된 mappingindex에 해당하는 folio를 페이지 캐시에서 검색합니다. 없으면 FGP_CREAT 플래그에 따라 새 folio를 할당하여 추가합니다.

분기 로직:

1. filemap_get_entry(mapping, index)로 XArray에서 folio/shadow entry 검색

2. 찾은 folio가 있고 FGP_LOCK이면:

- FGP_NOWAIT: folio_trylock() 실패 시 -EAGAIN 반환

- 일반: folio_lock() (블로킹)

- truncate 확인: folio->mapping != mapping이면 unlock 후 재시도

3. FGP_ACCESSED이면 folio_mark_accessed() 호출

4. 찾지 못하고 FGP_CREAT이면:

- order 결정: mapping_min_folio_order(mapping) ~ FGF_GET_ORDER(fgp_flags)

- filemap_alloc_folio()로 할당

- filemap_add_folio()로 XArray에 추가

- 실패 시 order를 줄여서 재시도 (최소 min_order까지)

2. `__filemap_add_folio` — XArray에 folio 추가

// mm/filemap.c:848-946
noinline int __filemap_add_folio(struct address_space *mapping,
        struct folio *folio, pgoff_t index, gfp_t gfp, void **shadowp)

역할: 할당된 folio를 mapping->i_pages XArray에 삽입합니다. 기존 shadow entry와 충돌 시 분기합니다.

분기 로직:

1. XA_STATE_ORDER로 XArray 상태 초기화

2. xas_lock_irq() 후 충돌 항목 반복 (xas_for_each_conflict)

3. 충돌 항목이 일반 folio(!xa_is_value)이면 -EEXIST 반환 (이미 존재)

4. shadow entry이면 기존 entry의 order 확인

5. 기존 entry가 더 크면(order > forder): xas_try_split()로 large entry를 smaller pieces로 분할

6. xas_store(&xas, folio)로 최종 삽입

7. 통계 업데이트: mapping->nrpages += nr, NR_FILE_PAGES, NR_FILE_THPS

3. `filemap_add_folio` — memcgroup 포함 folio 추가

// mm/filemap.c:949-990
int filemap_add_folio(struct address_space *mapping, struct folio *folio,
                      pgoff_t index, gfp_t gfp)

역할: memcgroup 체크 후 __filemap_add_folio()를 호출합니다. 추가 성공 시 LRU에 등록합니다.

흐름:

1. mem_cgroup_charge(folio, ...) — memcgroup 과금

2. __filemap_set_locked(folio) — folio 잠금

3. __filemap_add_folio() — 실제 XArray 삽입

4. 실패 시 memcgroup uncharge

5. 성공 시 workingset_refault()로 recently-evicted folio 활성화

6. folio_add_lru(folio)로 LRU 리스트에 추가

4. `filemap_read` — 파일 읽기

// mm/filemap.c:2768-2880
ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter, ssize_t already_read)

역할: 페이지 캐시에서 파일 데이터를 읽어 사용자 공간으로 복사합니다.

흐름:

1. 유효성 검사 (위치, 크기)

2. do-while 루프에서 반복:

- filemap_get_pages() — 읽을 folio 배치 가져오기 (readahead 포함)

- folio_mark_accessed() — 접근 표시 (LRU 활성화)

- copy_folio_to_iter() — 사용자 공간으로 데이터 복사

- folio_put() — 참조 해제

3. file_accessed(filp) — atime 업데이트

5. `filemap_fault` — mmap 페이지 폴트 처리

// mm/filemap.c:3512-3670
vm_fault_t filemap_fault(struct vm_fault *vmf)

역할: mmap된 파일 영역에 대한 페이지 폴트를 처리합니다. 페이지 캐시에서 folio를 찾고, 없으면 I/O로 읽어옵니다.

흐름:

1. 페이지 캐시에 folio 존재 여부 확인 (filemap_get_folio)

2. 존재하면 async readahead 시도 (do_async_mmap_readahead)

3. 존재하지 않으면:

- PGMAJFAULT 카운트 증가 (major fault)

- sync readahead 트리거 (do_sync_mmap_readahead)

- __filemap_get_folio(FGP_CREAT|FGP_FOR_MMAP)로 새 folio 생성

4. lock_folio_maybe_drop_mmap() — folio 잠금

5. folio_test_uptodate() 확인:

- Uptodate이면 vmf->page 설정 후 반환

- 아니면 filemap_read_folio()로 직접 읽기

6. VM_FAULT_MAJOR 또는 VM_FAULT_LOCKED 플래그 반환

6. `filemap_write_and_wait_range` — 더티 페이지 writeback

// mm/filemap.c:675-698
int filemap_write_and_wait_range(struct address_space *mapping,
                                 loff_t lstart, loff_t lend)

역할: 지정된 범위의 더티 페이지를 디스크에 기록하고 완료를 대기합니다.

흐름:

1. mapping_needs_writeback(mapping) 확인

2. 더티 페이지가 있으면 filemap_fdatawrite_range()로 writeback 시작

3. -EIO가 아니면 __filemap_fdatawait_range()로 완료 대기

4. filemap_check_errors()로 에러 확인

7. `filemap_remove_folio` — 페이지 캐시에서 folio 제거

// mm/filemap.c:250-264
void filemap_remove_folio(struct folio *folio)

역할: 잠금이 걸린 folio를 페이지 캐시에서 제거하고 해제합니다.

흐름:

1. BUG_ON(!folio_test_locked(folio)) — 잠금 확인

2. spin_lock(&mapping->host->i_lock) — inode 잠금

3. xa_lock_irq(&mapping->i_pages) — XArray 잠금

4. __filemap_remove_folio(folio, NULL) — XArray에서 제거

5. mapping_shrinkable(mapping)inode_lru_list_add() — inode LRU 추가

6. filemap_free_folio(mapping, folio) — a_ops->free_folio + folio_put_refs

filemap 잠금 순서 (Lock Ordering)

// mm/filemap.c:81-127 — 커널이 지키는 잠금 순서
// i_mmap_rwsem > page_table_lock > i_pages lock (일반 경로)
// mmap_lock > invalidate_lock > lock_page (폴트 경로)
// i_rwsem > mmap_lock (쓰기 경로)
경로잠금 순서
truncate`i_mmap_rwsem` → `private_lock` → `swap_lock` → `i_pages lock`
mmap fault`mmap_lock` → `invalidate_lock` → `lock_page(folio)`
일반 쓰기`i_rwsem` → `mmap_lock` → `page_table_lock` → `i_pages lock`
페이지 폴트`mmap_lock` → `page_table_lock` → `i_pages lock`

folio 잠금 공정성 (Page Lock Unfairness)

// mm/filemap.c:1076 — sysctl_page_lock_unfairness 기본값
static int sysctl_page_lock_unfairness = 5;

folio lock 대기 시 unfairness 카운터가 0 이하가 되면 WQ_FLAG_CUSTOM 플래그가 설정되어 공정한 잠금 양도(fair lock handoff)가 활성화됩니다. 이를 통해 thundering herd 문제를 완화합니다.

// mm/filemap.c:1267-1273
repeat:
    wait->flags = 0;
    if (behavior == EXCLUSIVE) {
        wait->flags = WQ_FLAG_EXCLUSIVE;
        if (--unfairness < 0)
            wait->flags |= WQ_FLAG_CUSTOM;  // 공정한 양도 활성화
    }
  • EXCLUSIVE 대기: 하나의 대기자만 깨움 (기본)
  • WQ_FLAG_CUSTOM: 잠금 소유자가 직접 양도 (공정성 보장)
  • sysctl vm.page_lock_unfairness로 런타임 조정 가능
  • 호출 흐름

    파일 읽기 흐름

    generic_file_read_iter()
      └─ filemap_read()
           ├─ filemap_get_pages()
           │    ├─ filemap_get_read_batch()     ← XArray에서 folio 배치 조회
           │    ├─ page_cache_sync_ra()          ← 없으면 sync readahead
           │    ├─ filemap_create_folio()        ← 그래도 없으면 새 folio 생성
           │    └─ filemap_readahead()            ← readahead 트리거
           ├─ folio_mark_accessed()              ← LRU 활성화
           └─ copy_folio_to_iter()               ← 사용자 공간으로 복사

    mmap 폴트 처리 흐름

    do_read_fault() / do_fault()
      └─ filemap_fault()
           ├─ filemap_get_folio()                ← 페이지 캐시 조회
           │    ├─ [hit] do_async_mmap_readahead()
           │    └─ [miss] do_sync_mmap_readahead()
           │         └─ __filemap_get_folio(FGP_CREAT)  ← 새 folio 생성
           ├─ lock_folio_maybe_drop_mmap()       ← folio 잠금
           ├─ folio_test_uptodate() 검사
           │    ├─ [uptodate] → vmf->page 설정
           │    └─ [not uptodate] filemap_read_folio() ← 직접 I/O
           └─ VM_FAULT_MAJOR | VM_FAULT_LOCKED 반환

    페이지 캐시 추가 흐름

    __filemap_get_folio_mpol()
      └─ filemap_add_folio()
           ├─ mem_cgroup_charge()                ← memcgroup 과금
           ├─ __filemap_set_locked()             ← folio 잠금
           ├─ __filemap_add_folio()              ← XArray 삽입
           │    ├─ xas_for_each_conflict()       ← 충돌 검사
           │    │    └─ xas_try_split()          ← large entry 분할
           │    └─ xas_store(folio)              ← 최종 삽입
           ├─ workingset_refault()               ← recently-evicted 활성화
           └─ folio_add_lru()                    ← LRU에 추가

    Writeback 흐름

    balance_dirty_pages_ratelimited()
      └─ balance_dirty_pages()
           ├─ balance_domain_limits()            ← global/memcg 도메인 제한 계산
           ├─ wb_start_background_writeback()    ← bg_thresh 초과 시 writeback 시작
           ├─ balance_wb_limits()                ← pos_ratio 계산
           └─ throttle 로직                       ← task_ratelimit에 따라 쓰기 throttling

    조건별 비교

    folio vs 기존 struct page

    항목struct pagestruct folio
    크기64 bytes (기본)64 bytes × N (compound)
    관리 단위개별 페이지하나 이상의 연속 페이지
    page cache 키page indexfolio index
    lock 범위개별 페이지folio 전체 (head page lock)
    dirty 추적개별 page flagfolio flag (전체 dirty 추적)
    compatibility모든 커널5.16+ (기존 API 래퍼 제공)
    large folio제한적 (compound_page)기본 지원
    rmap 추적anon_vma 체인 순회_mm_id 기반 O(1) 매핑 카운트 (Linux 7.0)
    memcg 과금개별 pagefolio 단위 (large folio도 1회 과금)

    FOLIO_MAPPING_ANON 플래그 (Linux 7.0):

    // folio->mapping의 LSB가 1이면 anonymous page
    // folio->mapping에서 실제 address_space는 ~1로 마스킹하여 얻음
    // anon_vma는 address_space 대신 사용

    파일 읽기 경로 비교

    시나리오경로특징
    일반 read`filemap_read()` → `filemap_get_pages()`페이지 캐시 히트 시 I/O 없음
    mmap fault (hit)`filemap_fault()` → `folio_test_uptodate()`async readahead 가능
    mmap fault (miss)`filemap_fault()` → `do_sync_mmap_readahead()`major fault, sync I/O
    direct I/O`generic_file_read_iter()` → `mapping->a_ops->direct_IO()`페이지 캐시 우회

    writeback 트리거 조건

    조건동작파라미터
    `nr_dirty > bg_thresh`백그라운드 writeback 시작`vm.dirty_background_ratio` (기본 10%)
    `nr_dirty > dirty_thresh`쓰기 프로세스 throttling`vm.dirty_ratio` (기본 20%)
    `dirty_expire_interval` 초과만료된 더티 페이지 즉시 writeback`vm.dirty_expire_interval` (기본 30s)
    `dirty_writeback_interval`kupdate 스타일 주기적 writeback`vm.dirty_writeback_interval` (기본 5s)
    `ratelimit_pages` 초과per-CPU throttling 체크`vm.dirty_writeback_interval` (기본 32 pages)
    // mm/page-writeback.c:68,75,92,103,110
    static long ratelimit_pages = 32;           // per-CPU throttling 임계값
    static int dirty_background_ratio = 10;     // 백그라운드 writeback 시작 비율
    static int vm_dirty_ratio = 20;             // 쓰기 throttling 시작 비율
    unsigned int dirty_writeback_interval = 5 * 100;  // centiseconds (5초)
    unsigned int dirty_expire_interval = 30 * 100;    // centiseconds (30초)

    folio lock 상태

    플래그의미관련 함수
    `PG_locked`folio lock 획득됨`folio_lock()`, `folio_trylock()`
    `PG_uptodate`데이터가 디스크와 일치`folio_end_read()`, `folio_mark_uptodate()`
    `PG_dirty`수정된 데이터 존재`folio_mark_dirty()`, `folio_clear_dirty_for_io()`
    `PG_writeback`writeback 진행 중`folio_start_writeback()`, `folio_end_writeback()`
    `PG_lru`LRU 리스트에 있음`folio_add_lru()`, `folio_del_lru()`
    `PG_active`활성 LRU에 있음`folio_set_active()`, `folio_clear_active()`

    자료구조 관계도

    Folio / Page Cache 자료구조 관계도

    호출 흐름 다이어그램

    Filemap Read / Fault 호출 흐름

    관련 문서

  • 메모리 관리 개요 — 전체 메모리 관리 조감도
  • Buddy Allocator — 물리 페이지 할당
  • SLUB 할당자 — slab 기반 작은 객체 할당
  • VMA / mmap — 가상 메모리 영역 관리
  • 페이지 회수 — LRU 기반 페이지 회수
  • vmalloc — 가상 연속 메모리 할당
  • Swap / zswap — 스왑 및 압축 캐시
  • tmpfs / shmem — 공유 메모리 파일 시스템
  • Workingset — Workingset 추적 (shadow entry)
  • Readahead — 페이지 캐시 readahead
  • minzkn.com 비교

    minzkn.com 메모리 관리 개요 페이지에서 struct page, folio 관련 내용을 확인한 결과:

    minzkn 핵심 내용:

  • struct page의 기본 필드 설명 (flags, lru, mapping, index, private, _refcount, _mapcount)
  • 주요 페이지 플래그 (PG_locked, PG_referenced, PG_uptodate, PG_dirty, PG_lru, PG_active, PG_slab, PG_compound)
  • folio 컨셉: "커널 5.16+에서 compound page를 효율적으로 다루기 위해 도입된 추상화"
  • 익명 페이지 vs 파일 기반 페이지 구분
  • 우리 문서에서 보강된 내용:

  • minzkn의 struct page 설명을 folio 관점에서 확장하여 Linux 7.0의 _mm_id, _deferred_list, _hugetlb_* 필드까지 포함
  • minzkn의 플래그 설명을 folio_mark_accessed(), folio_clear_dirty_for_io() 같은 folio API와 연결
  • minzkn의 "folio = head page 대표" 설명을 FOLIO_MATCH 정적 검증 코드로 검증
  • filemap 잠금 순서, folio lock 공정성 메커니즘은 minzkn에서 다루지 않는 심화 내용