Memory management framework—nodes and areas (3)

Article directory

  • 1. Memory management nodes
    • 1.1 Background introduction
    • 1.2 In-depth analysis
      • 1.2.1 Definition and structure of memory nodes
      • 1.2.2 Division and organization of memory nodes
      • 1.2.3 Memory node and processor association
      • 1.2.4 Operation and use of memory nodes
    • 1.3 Case Analysis/Practical Tips
      • 1.3.1 How to obtain the memory node corresponding to the current processor?
      • 1.3.2 How to initialize a new memory node?
  • 2. Memory management area
    • 2.1 Background introduction
      • Knowledge review: Why is Linux divided into three zones: ZONE_DMA ZONE_NORMAL ZONE_HIGHMEM?
    • 2.2 In-depth analysis
      • 2.2.1 Division of memory area
      • 2.2.2 struct zone structure
    • 2.3 Case Analysis/Practical Tips
  • Reference link

1. Memory management node

1.1 Background introduction

In the Linux kernel, memory management is an important part of the core system. To achieve efficient and stable memory management, the kernel uses a variety of technologies and data structures. Among them,Memory node (Node) is an important concept used to describe the distribution and organization of memory in the system. This article will delve into the related technologies and data structures of memory nodes in the Linux kernel.

1.2 In-depth analysis

1.2.1 Definition and structure of memory nodes

In the Linux kernel, the pglist_data structure is used to describe memory nodes. This structure is defined in the include/linux/mmzone.h file.

struct pglist_data {<!-- -->
    struct zone node_zones[MAX_NR_ZONES]; // Memory area array
    struct zonelist node_zonelists[MAX_ZONELISTS]; // Alternate zone list
    int nr_zones; //The number of memory areas contained in this memory node
#ifdef CONFIG_FLAT_NODE_MEM_MAP // Except for sparse memory model
    struct page *node_mem_map; // Page description array
    #ifdef CONFIG_PAGE_EXTENSION
        struct page_ext *node_page_ext; // Extended attributes of memory pages
    #endif
#endif
    unsigned long node_start_pfn; // The starting physical page number of this memory node
    unsigned long node_present_pages; // Total number of physical pages
    unsigned long node_spanned_pages; //The total size of the physical page range, including the memory hole size
    int node_id; // node identifier
};

1.2.2 Division and organization of memory nodes

In the Linux kernel, the CPU is divided into multiple nodes (nodes), and the memory is divided into clusters (banks). Each CPU corresponds to a local physical memory, that is, one CPU-node corresponds to a memory cluster bank. Each memory cluster is considered a node.

1.2.3 Memory node and processor association

Each memory node is associated with a processor in the system and is represented in the kernel as an instance of pg_data_t. Each node in the system is linked to a NULL-terminated pgdat_list linked list, and each node in it is linked to the next node using the node_next field of pg_data_t.
For machines with a UMA (unified memory access) structure like PC, only a static pg_data_t structure called contig_page_data is used.

1.2.4 Operation and use of memory nodes

The kernel provides a series of functions and data structures for operating and managing memory nodes. For example, the memory node corresponding to a given processor can be obtained through the get_node method, and the setup_node function in the mm/page_alloc.c file is used to initialize a new memory node. In addition, the kernel also provides functions such as free_area_init, build_zonelists and other functions for building and managing related data structures of memory nodes.

1.3 Case Analysis/Practical Tips

1.3.1 How to obtain the memory node corresponding to the current processor?

In the kernel code, the memory node corresponding to the current processor can be obtained through the CPU ID number. For example, you can get the memory node of the current CPU through the following code snippet:

struct pglist_data *pd = NODE_DATA(raw_smp_processor_id());

1.3.2 How to initialize a new memory node?

To initialize a new memory node, you can use the setup_node function in the mm/page_alloc.c file. This function accepts a pglist_data structure instance and a zonelist structure instance as parameters, and initializes the relevant data structure of the memory node. Here is an example code snippet:

struct pglist_data pd = {<!-- --> .node_id = 0, .node_start_pfn = 0 };
struct zonelist z = {<!-- --> .full_zones = 0, .zonelist = {<!-- --> } };
setup_node(NODE_DATA(0), & amp;pd, & amp;z);

2. Memory management area

include\linux\mmzone.h

2.1 Background introduction

Due to different hardware architectures of different computer types, the use of page frames is limited. The Linux kernel must deal with the following two hardware constraints of the 80×86 architecture:

  • The ISA bus’s direct memory access (DMA) processor has a strict limitation: it can only address the first 16MB of RAM.
  • The large capacity of RAM makes the linear address space too small, and not all physical space can be mapped to a unique linear address space.

In order to solve the above two limitations, Linux divides the system pages into different memory pools to form different memory areas. This division is a logical grouping and has no physical meaning to the kernel. The following explains how to use the struct zone structure to manage these memory areas.

Knowledge review: Why is Linux divided into three zones: ZONE_DMA ZONE_NORMAL ZONE_HIGHMEM?

  • The division of these areas mainly takes into account the limitations of the hardware architecture of different computer types.
  • The historical problem of the isa bus is that it can only access the first 16M of memory.
  • Large-capacity RAM makes the linear address space too small, and not all physical space can be mapped to a unique linear address space

2.2 In-depth analysis

2.2.1 Division of memory area

In the Linux kernel, memory is divided into three different areas, namely ZONE_DMA, ZONE_NORMAL and ZONE_HIGHMEM.

Area Description Physical Memory
ZONE_DMA This is the zone used for direct memory access (DMA) and its physical memory is located in the first 16MB of the system. The page frames in this area can only be directly accessed by the DMA device, and the kernel cannot directly access these page frames. <16MB
ZONE_NORMAL This is a normally addressable area with physical memory between 16MB and 896MB. The kernel has direct access to page frames in this area. 16~896MB
ZONE_HIGHMEM This is a dynamically mapped area, and its physical memory is above 896MB. The page frames in this area cannot be directly accessed by the kernel, but can be dynamically mapped and accessed through specific APIs. >896MB

2.2.2 struct zone structure

The kernel manages each memory area through the struct zone structure. This structure contains various information about the memory area, including the name of the area, spin lock, water level value, etc. The following is an explanation of some important members of the struct zone structure:
name: A NULL-terminated string indicating the name of the memory area. Initialization occurs during kernel startup.
lock: A spin lock used to protect the structure from concurrent access. Note that this lock only protects the structure itself, not all pages residing in this area.
watermark: An array containing the minimum, minimum and maximum water level values for the zone. The kernel uses these watermark values to set an appropriate baseline for memory consumption. These water level values are dynamically adjusted based on the amount of free memory on the system.

2.3 Case analysis/practical skills

How to determine which node or management area a page frame belongs to?
Indexed by the high bit of flag in each page frame descriptor. For example, the page_zone() function receives the address of the page descriptor as a parameter, returns the high bit of flag in the page descriptor, and determines the corresponding management in the zone_table[] array. The address of the zone descriptor. The specifics will depend on the actual operating system, but the following is just a simplified example.

#include <linux/mm.h>
/* Assume the address of the page frame descriptor is pmd */
struct page_md *pmd;
/* Call the page_zone() function to obtain the high index of the flag */
int index = page_zone(pmd);
/* Query the zone_table[] array to obtain the descriptor address of the corresponding management zone */
struct zone *zone = & amp;zone_table[index];
/* Output results */
printk("The page frame belongs to zone %s\
", zone->name);

Reference link

Physical memory organization structure
An in-depth introduction to the basics of Linux kernel memory management