Linux memory hierarchy

                 PHYSICAL MEMORY
                       |
                 Buddy allocator
                       |
                    Folio
                       |
       +---------------+----------------+
       |               |                |
   User mapping    Page cache        Kernel objects
       |               |                |
   mm/VMA/PT       inode/XArray       SLUB
       |
    process

and attached:

       Folio
        |
  +-----+-------+------+
  |             |      |
 LRU          memcg   RMAP

Not a complete map, and there are also considerations of NUMA that are not present.(source:ChatGPT because I’m bad at making trees and also lazy)

Basic layout: from physical to virtual

physical memory is divided into nodes in NUMA computers(with most computers, it is single node, but enabled nonetheless), and inside the nodes, it is divided into zones. Most memory space are pages from ZONE_NORMAL. Each zone’s pages are managed by buddy allocator, which allocates sizes of physical memory by power of two size. It manages 4KB, 8KB, 16KB size blocks up to 4MB, and each has list of available blocks. If none available, it allocates from bigger blocks by splitting it. Allocated pages could be used for user space and kernel. Kernel manages pages from buddy allocator with slub, which will be explained later.

Pages(folios in modern kernel, which will be explained later) are managed with pte, with multiple levels of directories, with top directory being page global directory. VMA(abbreviation for virtual memory address) defines a certain range of memory addresses that are mapped to physical memory, and multiple VMAs are formed to create the full memory mappings of a process. Each process has different memory mappings to implement virtual memory. mm_struct(collection of VMA and other descriptors) is just a descriptor for kernel’s virtual address space management, and page tables can be accessed with pgd(page global directory) object in mm_struct.

Physical Memory

Page(struct page)

page structures are kernel objects used as describes page frame. Each structure has page frame numbers which can be actual addresses with pfn_to_page(). Page structrures have flags that represent state of a page, 5 fields describe whether it is locked(PG_locked), is written back to disk storage(PG_writeback), data is changed and needs to be saved(PG_dirty), is valid(PG_uptodate), or is in LRU list(PG_lru). It also contains last cpuid it was used with.

Two types of pages exists, file-backed pages, and anonymous pages. File-backed pages are pages that are mapped from file’s data. mmap() is used to map filles to memory. When file is mapped, it is not directly available at memory, and it stays that way until user requests that address, and it page faults for the first time. When it page faults, page fault handler checks if that address is available in memory mappings, and then at that point file is loaded. It cannot access pages bigger than set size, unlike anonymous pages. Anonymous pages are what stores data generated during runtime that is not file.

Miscellaneous

Folios(struct folio)

Folio is a group of physically continuous pages. Size of it is bigger or equal the page size. It was implemented because compound pages created overhead of checking whether is it a part of compound page or not, whether it is head or not, and so on. Folios are guaranteed to be a page or a head of a compound page. It provided better interfaces for high level memory management page structures used to deal with, and is now a crucial component of memory management. Page can be a folio with page_folio(), and Nth page can be obtained with folio_page(folio, N).

The first member of folio is struct page, at offset 0, and other page structure slots are available for folio to manage tail pages’ description and metadata. It is designed this way so that struct folio * is always the head page of single page or compound page.