Disclaimer: This is mostly based on x86_64, sizes and design may vary across architectures.
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)
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 area) 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. pgd has multiple hierarchies of entries with pte at the end, in the order pgd→pud→pmd→pte. PTE accounts for 4KiB page, and pmd, pud, pgd, each stores 512 entries of each child entries. So pmd can hold 2MiB, pud 1GiB, and pgd 512GiB.
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.
_refcount is for usage counting, although not to be used directly, use with page_ref_add, page_ref_sub , etc.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).