Timeline
Timeline
2025-11-20
init
This article introduces the core mechanisms of Linux memory management, including the concepts of segmentation and paging, address concepts, and process address space layout. It further discusses virtual memory area management, physical memory initialization, and common data structures, and summarizes the multi-level page table mapping principles and address translation processes under ARM32 and ARM64 architectures.
About Addresses
Address Space Not Isolated
Malicious processes can arbitrarily modify the memory of other processes
Memory usage efficiency is very low, for example, when memory is scarce, all data of a process must be swapped to the swap partition
Early Memory Usage Methods
- Segmentation(Main idea: Map the virtual address space to the physical address space one-to-one; the entire program must be loaded into memory to run)
- Paging(Main idea: When a program runs, only the pages needed are allocated, i.e., on-demand allocation; pages not needed for a long time can be swapped to disk)
Address Concepts:
Logical Address(Intel-specific term, the part that generates the offset address related to segments)
Linear Address(Intel-specific term, an intermediate layer in converting logical addresses to physical addresses; in segmentation, the logical address is the segment offset, and adding the base address yields the linear address)
Virtual Address(Logical addresses and linear addresses are collectively referred to as virtual addresses)
Physical Address(The address required by the CPU to access physical memory via the external bus)
Process Address Space

TLB, also known as the translation lookaside buffer, caches page table entries for page table translations
Memory Management Overview

Memory Management from the Process Perspective:

Typical Virtual Address Space Layout of a 32-bit Linux System:
- User Space (0-3G):
- Code Segment & Data Segment: Stores the program’s executable instructions and initialized data.
- Heap Space: Used for dynamic memory allocation (e.g.,
malloc), grows towards higher addresses. - mmap Space: Used for file mapping, shared memory, etc., grows towards lower addresses.
- Stack Space: Used for function calls and local variables, grows towards lower addresses.
- Kernel Space (3G-4G):
- All processes share this portion of address space, used for kernel code, data, and temporary mappings.

Virtual Address Memory Layout - User Space (0-3G):
Physical Memory Mapping:
Linear Mapping: The kernel maps low-end physical memory (e.g.,
ZONE_NORMAL) is directly and linearly mapped to a fixed region of the kernel address space, offering fast access.High-end mapping: For high-end physical memory (e.g.,
ZONE_HIGHMEM), the kernel temporarily maps it to the high-end part of the kernel address space only when needed.
Process level:
task_structandmm_structtask_struct: Each process in the kernel is described by atask_structstructure, which contains all information about the process, and itsmmmember points to the process’s memory descriptor.mm_struct: This is the process’s memory descriptor, the core data structure for managing the process’s virtual address space. It mainly contains two key members:mmap: Points to the root of the process’s virtual memory area (VMA) linked list or red-black tree.pgd: Points to the process’s Page Global Directory, the top-level entry of the page table.
Virtual Memory Area (VMA) Management
VMA(Virtual Memory Area): The kernel uses the
vm_area_structstructure to describe a contiguous virtual address space. Each VMA represents a segment of virtual memory with the same attributes (e.g., readable, writable, executable), such as code segment, data segment, heap, stack, mmap mapping area, etc.mm_structthroughmmapMembers organize all VMAs, making it convenient for the kernel to search, manage, and operate.
mem_map[]Array: The kernel uses a globalmem_maparray to manage all physical memory pages, where each element is astruct pagestructure, representing a physical page frame.ZONE Management: Physical memory is divided into different memory management zones (ZONE), such as
ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEMetc., each zone has different purposes and management methods.

Common Data Structures

| Data Structure | Main Definition File | Supplementary Notes |
|---|---|---|
mm_struct | include/linux/mm_types.h | also appears ininclude/linux/mm.hwidely referenced and manipulated in |
vm_area_struct(VMA) | include/linux/mm_types.h | describes a contiguous virtual memory area |
struct page | include/linux/mm_types.h | defined in early kernel versions ininclude/linux/mm.h, moved to in newer versionsmm_types.h |
struct zone | include/linux/mmzone.h | describes physical memory zones (e.g., DMA, NORMAL, HIGHMEM) |
struct pglist_data(pgdata) | include/linux/mmzone.h | memory descriptor for a NUMA node, managing all zones under that node |
mem_map[] | mm/memory.c | global physical page array, its address determined during kernel initialization, each NUMA node also has its ownnode_mem_map |
pte_t/ page table entry | arch/arm64/include/asm/pgtable-types.h | page table entry types and page table operations are architecture-dependent |
struct mm_struct
1 | // include/linux/mm_types.h |
struct vm_area_struct
1 | // include/linux/mm_types.h |
struct page
1 | // include/linux/mm_types.h |
struct zone
1 | // include/linux/mmzone.h |
struct pglist_data
1 | // include/linux/mmzone.h |
mem_map
1 | // mm/memory.c |
Initialization of Linux physical memory
- How does the ARM Linux kernel know the size of memory in the system during boot?
- In a 32-bit Linux kernel, the user space to kernel space ratio is usually 3:1; can it be changed to 2:2?
- How are physical memory pages added to the buddy system? Are they added one by one or in powers of two?
Introduction to DDR
- Bank
- row
- Column


Page table mapping
arm32
- Supports four-level mapping
- Global directory entryPGD (Page Global Directory)
- Upper directory entryPUD (Page Upper Directory)
- Middle directory entryPMD (Page Middle Directory)
- Page table entry (Page Table)
The Linux kernel was originally based on x86, so the kernel code commonly uses the above terms to refer to first-level page tables, second-level page tables, etc.
Translation from virtual address to physical address

First-level page table entry

- Fault
- Invalid entry
- Page table
- Entry of the first-level page table
- bit[0~1]: Used to indicate whether this page table entry is a first-level page table entry or a segment mapping entry
- PXN: Whether PL1 can execute this code, 0 means executable, 1 means not executable
- NS: non security bit indicates the bit for security extension
- Domain: Indicates the domain it belongs to, Linux only uses 3 domains
- bits[31:10] Page table base address points to the base address of the second-level page table

- Section
- Segment table entry for segment mapping
Second-level page table entry

- bit0 Execute Never flag, 1 means execute never, 0 means executable
- bit1 Distinguishes between large pages and small pages
- C/B bit: Memory region attributes
- TEX[2:0]: Memory region attributes
- AP[1:0]: Access permissions
- S: Whether shareable
- nG: Used for TLB
arm64
ARMv8-AArchitecture
- Supports 48 address lines
- The access area is divided into two parts (kernel space and user space), each 256TB
- Supports 4KB, 16KB, and 64KB pages, with 3-level or 4-level mapping
ARMv8 has two page table base addresses, one for user space and one for kernel space, selected by bit 63
4KB page size + 4-level mapping

TODO
- Summarize macros for page table related operations
- start_kernel()->setup_arch()->paging_init()->map_lowmem()
static void __init create_mapping(struct map_desc *md)
Establish page table mapping for the kernel image region and linear mapping region
Study how to establish kernel page table mapping
- int remap_pfn_range() Study how to establish page table mapping using vma, virtual address, and physical address pfn
- static int do_anonymous_page Study this page fault handler to observe: how to set up the user process page table given vma, virtual address vaddr, and page table entries

ARM32 implementation: parallel page tables
Two sets of page tables: one to accommodate hardware, one to accommodate the Linux kernel

Page allocation mechanism
Buddy system
- The buddy algorithm allocates memory blocks in powers of two, and these blocks are called buddies
- What is a buddy
- Two blocks of the same size
- Two blocks with contiguous addresses
- Two blocks must be split from the same larger block

Migration types
- To solve the fragmentation problem, migration types were introduced into the buddy system
- MIGRATE_UNMOVABLE: has a fixed position in memory and cannot be moved arbitrarily, such as memory allocated by the kernel
- MIGRATE_MOVABLE: can be moved arbitrarily, such as memory allocated by user-space applications
- MIGRATE_RECLAIMABLE: cannot be moved but can be deleted and reclaimed, such as file mappings
- Generation of memory fragmentation

Page 3 is used by functions allocated by the kernel itself, such as alloc_page(GPF_KERNEL), so it belongs to non-movable pages. Although other pages are free, pages 0 to 7 cannot be combined into a large memory block
Page allocation and allocation mask
Core page allocation function
1 | struct page *alloc_pages(gfp_t gfp_mask,unsigned int order) |
gfp_maskFlag bits


- The page allocator is designed based on zones, so it is necessary to determine which zones can be used for this page allocation. The system will prioritize using ZONE_HIGHMEM, followed by ZONE_NORMAL
- During page allocation, it is also necessary to know from which migration type the memory is to be allocated
Zone watermarks

Defined in the Linux kernel:
1 | enum zone_watermarks { |
Page release function
1 | void free_pages(unsigned long addr,unsigned int order) |
per-CPU high-speed page cache
The kernel frequently requests and releases single page frames, such as for network card drivers, etc.
When the page allocator allocates or releases pages, it needs to acquire a lock: zone->lock
- To improve the efficiency of requesting and releasing single page frames, the kernel establishes a per-CPU page cache pool
- It stores several pre-allocated page frames
When requesting a single page frame, it is taken directly from the local CPU’s page frame cache pool
- No need to acquire a lock
- No need to perform complex page frame allocation operations
(This demonstrates the advantage of pre-establishing a cache pool, and each CPU has an independent cache)
per-CPU data structure
- In the zone, there is a member field ‘pageset’ pointing to the per-CPU cache
- struct per_cpu_pages data structure
1 | struct per_cpu_pages { |
slab mechanism
When needing to allocate small chunks of memory of a few dozen bytes in the kernel, what to do?

1 | /* |
- array_cache: This refers to the per-CPU object cache pool, one per CPU
- Used to create a local object cache pool, one per CPU, which helps:
- Allows objects to use the cache on the same CPU as much as possible, improving efficiency
- No additional spin locks needed, avoiding lock contention
- Used to create a local object cache pool, one per CPU, which helps:
1 | /* |
- batchcount: Indicates that when the local object cache pool is empty, batchcount objects need to be fetched from the shared cache pool to the local cache pool
- limit: When the number of free objects in the local cache pool exceeds limit, some objects need to be released
- shared: Used for multi-core systems
- size: Indicates the length of the object
- flags: Object allocation mask
- num: Number of objects that can be in one slab
- gfporder: A slab occupies multiple physical pages of 2 to the power of order
- colour: Indicates that a slab has multiple cache lines for coloring
- freelist_size: Each object occupies 1 byte to store the freelist
- name: Name of the descriptor
- object_size: Actual size of the object
- align: alignment length
composition of a slab

slab operation mechanism

slab reclamation
- If a slab descriptor has many free objects, should the system reclaim some free cached objects to release memory back to the system?
- using kmem_cache_When freeing an object, if the number of free objects in the local and shared object buffer pool (ac->avail) is greater than or equal to the pool’s limit (ac->limit), the system actively releases batchcount objects.
- The slab system also registers a timer to periodically scan all slab descriptors and reclaim some free objects.
kmalloc function
- The core of the kmalloc() function is the slab mechanism
- Create multiple slab descriptors according to the order of 2 for memory blocks
- void *kmalloc(size_t size, gfp_t flags)
- void kfree(const void*)
vmalloc


vmalloc interface function
1 | void *vmalloc(unsigned long size); |
vmalloc can sleep during allocation, so it cannot be used in interrupt context
VMA operations

1 | struct vm_area_struct { |

1 | struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr); |

1 | int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma) |

malloc
malloc stands for memory allocation, Chinese name dynamic allocation, an API mainly used in user space to allocate virtual memory
calloc automatically initializes the allocated memory space to 0, while malloc does not initialize it, and the data inside is random garbage data
realloc modifies the size of a previously allocated memory block, allowing it to be expanded or shrunk
malloc is an API encapsulated in the C library, ultimately calling the brk system call
glibc maintains a small warehouse; the implementation of the malloc function maintains a local small warehouse for the user process. When the process needs more memory, it requests from this small warehouse. If the warehouse stock is insufficient, it wholesales from the kernel through the agent brk
Think of malloc() as retail, then brk is the agent
brk system call
1 | SYSCALL_DEFINE1(brk, unsigned long, brk) |


- The above diagram searches for a free space in the heap area of the process’s address space, then creates a VMA, and then returns
- on-demand page
- Memory allocated by malloc may not be used for a long time, so it is actually not a good idea to immediately allocate physical space when a process allocates memory.
- Solution: page fault interrupt, where physical memory is created only when the process truly needs to access these virtual pages, as a last resort.
mlock system call
- The mlock system call allows a program to lock part or all of its address space in physical memory; calling mlock immediately allocates physical memory for the virtual memory.
- This will prevent Linux from swapping this memory page to swap space.
- In the implementation of the brk function, when finally returning the virtual memory address, it checks a variable VM_LOCKED, this VM_LOCKED is usually set from the mlock system call.
- If it is set, then mm_populate() needs to be called to immediately allocate physical memory and establish the mapping.
- The general case is: the work of allocating physical pages is always deferred until the user process needs to access these virtual pages, and only when a page fault occurs is physical memory allocated and a mapping established with the virtual address.

get_user_pages() function
- A very important interface function for allocating physical memory; many drivers use this API to allocate physical memory for user-space programs.

follow_page() function
- Returns the struct page data structure of a normal mapping page that has already been mapped in the VMA of the user process address space
vm_normal_page() function
- This function divides pages into two categories: normal pages and special pages
- Normal pages typically refer to pages with normal mappings, such as anonymous pages, page cache, and shared memory pages
- Special pages typically refer to pages with abnormal mappings; these pages are not intended to participate in memory management’s reclamation or merging functions, such as pages mapped with the following characteristics:
- VM_IO: Maps memory for I/O devices
- VM_PEN_MAP: Pure PFN mapping
- VM_MIXEDMAP: Fixed mapping
Summary
- The malloc function actually allocates process address space in user space; in kernel terms, it allocates a VMA, which is like an empty cardboard box. So when do we put things into the box? Two ways:
- One is to put things into the box only when it is actually used
- The other is to put what you want into the box at the time of allocation
- If two processes have the same virtual address allocated by malloc, will they conflict?
- Each user process has its own page table, and each process has an mm_struct data structure containing its own page table, a red-black tree and linked list for managing VMAs
- Even if process A and process B use malloc to allocate memory and get the same virtual address, they are actually two different VMAs, managed by two different sets of page tables.

mmap
1 |
|
- addr: Used to specify the starting address for mapping into the process address space. For application portability, it is usually set to NULL, allowing the kernel to choose an appropriate address.
- length: Indicates the size of the mapping into the process address space.
- prot: Used to set the read/write permissions of the memory-mapped region.
- PROT_EXEC: Indicates that the mapped pages are executable.
- PROT_READ: Indicates that the mapped pages are readable.
- PROT_WRITE: Indicates that the mapped pages are writable.
- PROT_NONE: Indicates that the mapped pages are inaccessible.
- flags: Used to set the attributes of the memory mapping, such as shared mapping, private mapping, etc.
- MAP_SHARED: Creates a shared mapping region. Multiple processes can map a file via shared mapping, so other processes can see changes to the mapped content, and modifications are synchronized to the disk file.
- MAP_PRIVATE: Creates a private copy-on-write mapping. Multiple processes can map a file via private mapping, so other processes do not see changes to the mapped content, and modifications are not synchronized to the disk file.
- MAP_ANONYMOUS: Creates an anonymous mapping, i.e., a mapping not associated with a file.
- MAP_MAP_FIXED: Creates a mapping using the parameter addr. If the kernel cannot map the specified address addr, mmap will fail. The parameter addr must be page-aligned. If the process address space specified by addr and length overlaps with an existing VMA region, the kernel will call do_munmap() to destroy the overlapping region and then remap the new content.
- MAP_POPULATE: For file mappings, it pre-reads the file content into the mapped region in advance. This feature only supports private mappings.
- fd: indicates this is a file mapping, fd is the open file handle
- offset: indicates the file offset during file mapping
File mapping and anonymous mapping
- Anonymous mapping: no corresponding file is mapped, the content of this mapped memory region is initialized to 0
- File mapping: the mapping is associated with an actual file, typically mapping the file’s content into the address space so that applications can read and write the file like operating system process space
Private mapping and shared mapping
- Private mapping: the created mapping is only visible to itself, other processes cannot see changes to the mapped content
- Shared mapping: other processes can see changes to the mapped content; if a file is opened in shared mode, modified content is synchronized to the disk file


Page fault
Paging mechanism
- MMU
- PTE_PRESENT bit in page table entry

PTE page table entry The ARM32 MMU has the following two registers related to memory access faults
- Fault Status Register (Data Fault Status Register, FSR)
- Data Fault Address Register (FAR)
The ARMv7 assembly processing flow isvectors_start -> vector_dabt -> datb_user / dabt_svc -> dabt_helpper -> v7_early_abort
The struct fsr_info data structure is used to describe the handling scheme corresponding to a fault status.
The fs_info[] array lists common address fault handling schemes.


Question 1: What is an anonymous page?
- In the Linux kernel, pages not associated with file mappings are called anonymous pages (anon pages). For example, pages allocated by malloc.
Question 2: Why is a page fault for anonymous pages needed?
- Without the page fault for anonymous pages, when you use the malloc() API in user space to allocate virtual memory, the kernel would have to actually allocate physical memory for it. The main issue is waste, because often applications allocate virtual memory but do not use it immediately, so there is no need to satisfy the application’s demand right away.
Question 3: What are the conditions for a page fault on anonymous pages?
- When the PRESENT bit in the PTE page table entry is not set
- The PTE content is empty
- The vma->vm_ops->fault() function pointer is not specified
When these three conditions are met, we determine it is a page fault for anonymous pages, and the handling function is: do_anonymous_page

- Question 1: What is a file mapping?
- A mapping associated with a file is essentially a file mapping, which corresponds to anonymous mapping. It maps the content of a file into the process address space.
- Question 2: What types of mappings cause page faults for file mappings?
- The corresponding file mapping mainly involves mapping ordinary files, such as a video player reading a video file.
- Another case is when a device driver maps a device DMA buffer into the process address space via mmap.
- In the handle_pte_How to determine if it is a file mapping in the fault() function
- When the page is not in memory
- The page table entry is empty
- The VMA defines the fault method function (vma->vm_ops->fault())
Write page fault
- Write page fault is the most complex case and also the most common scenario.
- Copy-on-Write (COW) technology
- When forking a child process, the kernel does not need to copy the entire user space content of the parent process to the child process. Instead, it allows the parent and child processes to share the parent’s address space, saving the time-consuming copy operation and only copying the page tables.
- When either the parent or child process needs to write, the data is copied, so that each process has its own copy. Therefore, the do_wp_page function here handles this sharing issue.
- do_wp_What are the judgment conditions for page()?
- When the PTE_PRESENT bit is set, and then the page fault handling flags indicate a write protection error (this write protection error is usually reported by hardware).

- Overall, this process is quite complex and needs to handle many situations:
- Normal mapping pages and special mapping pages
- Pure anonymous pages, this term is fabricated, meaning anonymous pages excluding KSM.
- Single anonymous pages, this term is also fabricated, meaning only one process’s virtual page maps this anonymous page, i.e., mapcount=0.
- Non-single anonymous pages
- Writable shared anonymous pages, this usually refers to pages with writable attributes and are shared, typically page cache.
Summary
- After a page fault occurs, based on the PRESENT bit of the PTE entry, whether the PTE content is empty (pte_none() macro), and whether it is a file mapping, the corresponding handling functions are as follows:
- Anonymous page fault handling do_anonymous_page()
- Judgment condition
- The PRESENT bit in the PTE entry is not set
- The PTE content is empty
- The vma->vm_ops->fault() function pointer is not specified
- Application scenario: malloc() allocates memory
- Judgment condition
- File mapping page fault handler do_fault()
- Judgment condition
- The PRESENT bit in the PTE page table is not set
- The PTE content is empty and the vma->vm_ops->fault() function pointer is specified
- do_fault() belongs to the case of page faults occurring in file mappings
- If only a read error occurs, then call do_read_fault() function to read this page
- If a write-protection error occurs in a private mapping VMA, copy-on-write occurs: a new page new_page is allocated, the content of the old page is copied to the new page, a PTE entry is generated from the new page and set into the hardware page table entry. This is the so-called copy-on-write (COW)
- If a write-protection error occurs in a shared mapping VMA, a dirty page is generated, and the system’s writeback mechanism is called to write back this dirty page
- Application scenarios:
- Using mmap to read file content, for example, using mmap in a driver to map device memory to user space
- Dynamic library mapping, for example, different processes can share a dynamic library through file mapping
- Judgment condition
- Swap page fault handler do_swap_page()
- Copy-on-write (COW) page fault handler do_wp_page()
- do_wp_There are two cases for the final processing of page()
- Reuse old_page: single anonymous pages and writable shared pages
- Copy-on-write: non-single anonymous pages, read-only or non-shared file-mapped pages
- Judgment condition: the PRESENT bit in the PTE is set and a write fault page fault occurs
- Application scenario: fork, the parent process forks a child process, both share the parent’s anonymous pages; when one needs to modify content, COW occurs
- do_wp_There are two cases for the final processing of page()
- Anonymous page fault handling do_anonymous_page()
pages data structure
When the MMU is enabled, the smallest unit of memory accessed by the CPU is a page

- Flag: PG_* flags
- Defined in include/linux/page-flags.h

ARM Versatile Express platform

- _count field
- When the value of _count is 0, it indicates that the page is free or about to be released
- When the value of _count is 0, it indicates that the page has been allocated and is being used by the kernel, and will not be released temporarily.
1 | static inline void get_page(struct page *page); |
_mapcount field
_The mapcount reference count indicates the number of process mappings to this page, i.e., how many user PTE page tables have mapped it. In a 32-bit Linux kernel, each user process has 3GB of virtual space and an independent page table, so it is possible for multiple user process address spaces to map to the same physical page simultaneously. The RMAP reverse mapping system leverages this feature._The mapcount reference count is mainly used in the RMAP reverse mapping system.
- _mapcount == -1 indicates that no PTE maps to the page.
- _mapcount == -1 indicates that only the parent process has mapped the page. When an anonymous page is first allocated,_The mapcount reference count is initialized to 0.
Usage:
The kernel code does not directly check the count and mapcount values; instead, it uses two macros.
static inline int page_mapcount(struct page *page)
static inline int page_count(struct page *page)
mapping field
- When this page is used for file cache (i.e., page cache), mapping points to the address space associated with this page cache._space object, this address_The address space object belongs to a memory object (such as a collection of pages for an inode).
- When this page is used as an anonymous page, mapping points to an anon_vma data structure, primarily used for reverse mapping.
lru field
- Used in the LRU list algorithm for page reclamation, the LRU list algorithm defines multiple lists
- Used to add a slab to the slab full list, slab free list, and slab partial list
virtual field
- A pointer to the virtual address corresponding to the page
The significance of struct page
- The kernel knows the current state of the page (via the flags field)
- The kernel needs to know whether a page is free (i.e., whether it has been allocated), and how many processes or memory paths are using this page (using count and mapcount reference counts)
- The kernel knows who is using this page, such as whether the user is an anonymous page from a user-space process or a page cache (via the mapping field)
- The kernel knows whether this page is used by the slab mechanism (via fields such as lru and s_mem)
- The kernel knows whether this page is linearly mapped (via the virtual field)

- Page lock PG_Locked
- The flags member of the struct page data structure defines a flag bit PG_locked, the kernel typically uses PG_locked to set a page lock
- The lock_page() function is used to acquire a page lock. If the page lock is held by another process, it will sleep and wait.
- If trylock_page() returns false, it means acquiring the lock failed; if it returns true, it means acquiring the lock succeeded.
- trylock_page() does not sleep and wait; it is only used for checking.
- The flags member of the struct page data structure defines a flag bit PG_locked, the kernel typically uses PG_locked to set a page lock
RMAP Reverse Mapping Mechanism
Forward Mapping
- From virtual address to physical address, following the steps of the MMU hardware.

RMAP Reverse Mapping When physical memory is insufficient?
- Virtual memory is often larger than physical memory.
- Swap temporarily unused physical memory to the swap partition.
How to determine which physical memory is temporarily unused?
- LRU Algorithm
- Second Chance Algorithm
Linux 2.4 kernel approach
- Traverse the VMAs of all processes to determine the PTE mapping a specific physical page.
Design goals of reverse mapping:

Data structures used in reverse mapping:

The RMAP tetralogy (using Linux 2.6.11 as an example)
Parent process allocates anonymous pages
- do_anonymous_page()->page_add_anon_rmap()
- page_add_anon_rmap()
- page->mapping points to the anon_vma data structure of the VMA
- Calculate page->index
Parent process creates child process
- do_fork()->copy_mm()->dump_mm(), copies all VMAs of the parent process to the corresponding VMAs of the child process
- anon_vma_link(): adds the child process’s VMA to the parent process’s vma->anon_vma->head linked list
Child process undergoes COW
- When parent and child processes share anonymous pages, the child process’s VMA undergoes COW
Page fault -> handle_pte_fault()->do_wp_page()-> allocate a new anonymous page -> page_add_anon_rmap()
The newly allocated anonymous page’s page->mapping points to the parent process’s vma->anon_vma
RMAP Applications
- Page Reclamation: Disconnect all mapped user PTEs to reclaim the page
- Page Migration: Disconnect all mapped user PTEs

Disadvantages:

Optimization: Reduce lock granularity

- Reverse Mapping Summary
- Improve page reclamation efficiency
- Consumes some memory space, a typical trade-off of space for time
- A bridge connecting virtual memory management and physical memory management
Page Reclamation
Page Replacement
page reclaim
other examples of page replacement
- processor cache
- web server
introduction to page reclaim algorithms
- optimal page replacement algorithm
- first-in first-out page replacement algorithm
- not recently used page replacement algorithm
- LRU algorithm (Least Recently Used page replacement algorithm)
- exploiting locality principle
- maintaining a linked list
- new page added to the head of the linked list
- whenever a page in the linked list is accessed, that page is moved to the head of the linked list
- whenever page replacement is needed, a page is taken from the tail of the linked list
- second chance algorithm
- Give the page a second chance to be accessed
- Clock page replacement algorithm
- Working set algorithm
Page reclamation algorithm used in the Linux kernel
- LRU linked list
- Inactive anonymous page LRU list_INACTIVE_ANON
- Active anonymous page LRU list_ACTIVE_ANON
- Inactive file-mapped page LRU list_INACTIVE_FILE
- Active file-mapped page LRU list_ACTIVE_FILE
- Non-reclaimable page LRU list (LRU_UNEVICTABLE)
- LRU linked list

LRU is implemented based on zones. Each zone has a complete set of LRU lists
Second chance method
- Disadvantage of LRU algorithm: It does not consider the frequent usage of the page, and the page may still be evicted from the LRU list
- Second chance method:
- An access status bit (hardware-controlled bit) is set
- Check the access bit of the page
- If it is 0, immediately evict it from the LRU list
- If it is 1, it means the page has been accessed again during this period. Clear the access status bit, then re-add the page to the head of the list, just like a newly added page
- The Linux kernel uses the following status bits to implement the second-chance algorithm:
- PTE_YONG: a hardware bit. When a page is accessed, the hardware automatically sets this bit
- PG_active: a software bit
- PG_referenced: a software bit
Linux page reclamation block diagram


Anonymous page reclamation process:
Page cache reclamation process

Migration of LRU lists
- The LRU list can migrate between the active list and the inactive list.
- Mainly used hardware and software bits.
- PTE_YOUNG: A hardware bit that is automatically set by the hardware when a page is accessed.
- PG_active: A software bit.
- PG_referenced: A software bit.
- Main helper functions.
- mark_page_accessed(): Mainly used to mark that a page has been accessed, then sets PG_active and PG_referenced
- page_referenced(): Determines whether a page has been accessed or referenced, returns the number of accessed/referenced PTEs, using the reverse mapping (RMAP) system to count the number of accessed/referenced PTEs.
- page_check_references()
kswapd kernel thread
- Responsible for asynchronously reclaiming pages when memory is insufficient.
- One kswapd kernel thread is created per NUMA memory node.
- alloc_pages() at low watermark (ALLOC_WMARK_LOW) cannot allocate memory, at which point the memory allocation function calls wakeup_kswapd() to wake up the kswapd kernel thread.

- When the zone is at a high watermark, kswapd is put to sleep
Detailed page reclamation flow chart

Lifecycle of anonymous pages
- Memory allocated via malloc/mmap interface -> do_anonymous_page()
- Copy-on-write: When a page fault occurs due to a write protection error, the newly allocated page is an anonymous page
- do_swap_When page() reads data back from the swap partition, a new anonymous page is allocated
- Migrate page
Allocate
- do_anonymous_page() allocates an anonymous page anon_Taking page as an example, anon_The state of a newly allocated page is as follows:
- page->_count = 1
- page->_mapcount=0
- Set the PG_swapbacked flag
- Add to LRU_ACTIVE_In the ANON linked list
- page->mapping points to the anon_vma data structure in the VMA
Use
- After the anonymous page is allocated during a page fault, the mapping between the process’s virtual address space VMA and the physical page is established. When a user process accesses the virtual address, it accesses the content of the anonymous page.
Swap out
Active list -> Inactive list
First scan of the inactive list
- add_to_The swap() function allocates swap space for this page

add_to_swap - try_to_unmap()

try_to_unmap - pageout()

Second scan of the inactive list
Assume that during the second scan of the inactive list, writing this page to the swap partition has completed. The callback function end of the block layer_swap_bio_write()->end_page_writeback() performs the following actions:
- Clear the PG_writeback flag
- Wake up threads waiting on this page’s PG_writeback threads, see wake_up_page(page,PG_writeback) function
shrink_page_list()->__remove_The mapping() function works as follows
- page_freeze_refs(page,2) checks if the current page->_count is 2, and sets the count to 0
- Clear the PG_swapcache flag
- Clear the PG_locked flag

__remove_mapping
Finally, add the page to free_In the page list, release the page, so this anon_The state of the page is that its content has been written to the swap partition, and the actual physical page has been released
Swap-in of anonymous pages
- After an anonymous page is swapped out to the swap partition, if the application needs to read or write this page, a page fault occurs. Because the present bit in the PTE indicates the page is not in memory, but the PTE entry is not empty, indicating the page is in the swap partition, so do is called_swap_The page() function re-reads the content of the page
Release of anonymous pages
- When a user process is closed or exits, all VMAs of the user process are scanned and cleared. If they meet the release criteria, the relevant pages will be released

Page migration
NUMA:Non-Uniform Memory Access
Each processor hasits own local memory (Node Local Memory)。
It can also access other processors’ memory (Remote Memory), but at a slower speed.
Commonly found inmulti-socket servers, large multi-core systems。

UMA: Uniform Memory Access
All processors share a single physical memory。
Access speed is uniform, that is,the latency of accessing any memory is the same。
Commonly found insmall multi-core systems、Symmetric Multiprocessor Systems (SMP)。
libnuma

Migrate all pages of a process from one memory node to another memory node
pid the PID of the process
maxnode: maximum number of nodes
old_nodes: a bit mask pointing to nodes, which are the nodes where the process resides
new_nodes: a node mask pointing to the migration destination

migrate some pages of the process to new memory nodes
migrae_pages



MIGRATE_ASYNC asynchronous mode, non-blocking
MIGRATE_SYNC synchronous mode, the process will block

page migration process

memory compaction
memory compaction
causes of memory fragmentation
- the physical memory of the Linux kernel is managed by the buddy system
- the buddy system consists of 11 linked lists of size 2^order, with order ranging from 0 to 10
- the buddy system has a habit of cutting the cake
- the buddy system can have a magical repair function


KSM

How to use KSM
- Enable KSM:
- madvise(addr,length,MADV_MERGEA_BLE)
- Disable KSM:
- madvise(addr,length,MADV_UNMERGEABLE)
- The mmap function in Android’s bionic library enables KSM by default

KSM statistics
- run: Writing 1 to this node starts the ksmd kernel thread, writing 0 stops it
- pages_to_scan: Number of pages scanned per batch, how many pages the ksmd kernel thread scans each time it is woken up
- sleep_millisecs: How long ksmd sleeps before the next scan, in milliseconds
- pages_shared: Number of shared pages. If 1000 pages all have the same content and are merged into one page, here pages_shared equals 1
- pages_sharing: Number of sharable pages. If 1000 pages are merged into one page, then pages_sharing is 1000
- pages_unshared: Number of pages currently not merged, typically the number of unstable pages
- full_scans: Number of full scans from start to end
