Nodes 表示node的数据结构为pg_data_t, 也就是struct pglist_data, 这个结构定义在<linux/mmzone.h>中: typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES]; struct zonelist node_zonelists[MAX_ZONELISTS]; int nr_zones; struct page *node_mem_map; struct bootmem_data *bdata; unsigned long node_start_pfn; unsigned long node_present_pages; /* total number of physical pages */ unsigned long node_spanned_pages; /* total size of physical page range, including holes */ int node_id; wait_queue_head_t kswapd_wait; struct task_struct *kswapd; int kswapd_max_order;} pg_data_t;node_zones: 分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEMnode_zonelists: 分配内存操作时的区域顺序,当调用free_area_init_core()时,由mm/page_alloc.c文件中的build_zonelists()函数设置。nr_zones: node中的zone的数量,1到3个之间。并不是所有的node都有3个zone的,比如有些就没有ZONE_DMA区域。node_mem_map: node中的第一个page,它可以指向mem_map中的任何一个page。bdata: 这个仅用于boot 的内存分配,下面再描述node_start_pfn: pfn是page frame number的缩写。这个成员是用于表示node中的开始那个page在物理内存中的位置的。 2.4以前的版本,用物理地址来表示的,后来由于硬件的发展,物理内存很可能大于32bit所表示 4G的内存地址,所以改为以页为单位表示。node_present_pages: node中的真正可以使用的page数量node_spanned_pages: node中所有存在的Page的数量,包括可用的,也包括被后面讲到的mem_map所占用的,dma所占用的区域的。(做了修正)英文原版是这么描述的:"node spanned pages" is the total area that is addressed by the node, includingany holes that may exist.可能是包括hold的node可以访问的区域的数量吧。node_id: node的NODE ID,从0开始kswapd_wait: node的等待队列对于单一node的系统,contig_page_data是系统唯一的node数据结构对象。Zone 每个zone都由一个struct zone数据结构对象描述。zone对象里面保存着内存使用状态信息,如page使用统计,未使用的内存区域,互斥访问的锁(LOCKS)等。struct zone在<linux/mmzone.h>中定义(把不关心的NUMA和memory hotplug相关的成员给省略掉了): struct zone { unsigned long free_pages; unsigned long pages_min, pages_low, pages_high; unsigned long lowmem_reserve[MAX_NR_ZONES]; struct per_cpu_pageset pageset[NR_CPUS]; spinlock_t lock; struct free_area free_area[MAX_ORDER];
ZONE_PADDING(_pad1_) //用于字节对齐
spinlock_t lru_lock; struct list_head active_list; struct list_head inactive_list; unsigned long nr_scan_active; unsigned long nr_scan_inactive; unsigned long nr_active; unsigned long nr_inactive; unsigned long pages_scanned; int all_unreclaimable;
atomic_t reclaim_in_progress;
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
int prev_priority;
ZONE_PADDING(_pad2_) //用于字节对齐 wait_queue_head_t * wait_table; unsigned long wait_table_hash_nr_entries; unsigned long wait_table_bits;
struct pglist_data *zone_pgdat; unsigned long zone_start_pfn;
unsigned long spanned_pages; unsigned long present_pages;
PFN是物理内存以Page为单位的偏移量。系统可用的第一个PFN是min_low_pfn变量,开始与_end标号的后面,也就是kernel结束的地方。在文件mm/bootmem.c中对这个变量作初始化。系统可用的最后一个PFN是max_pfn变量,这个变量的初始化完全依赖与硬件的体系结构。x86的系统中,find_max_pfn()函数通过读取e820表获得最高的page frame的数值。同样在文件mm/bootmem.c中对这个变量作初始化。e820表是由BIOS创建的。 x86中,max_low_pfn变量是由find_max_low_pfn()函数计算并且初始化的,它被初始化成ZONE_NORMAL的最后一个page的位置。这个位置是kernel直接访问的物理内存,也是关系到kernel/userspace通过“PAGE_OFFSET宏”把线性地址内存空间分开的内存地址位置。(原文:This is the physical memory directly accessible by the kernel and is related to the kernel/userspace split in the linear address space marked by PAGE OFFSET.)我理解为这段地址kernel可以直接访问,可以通过PAGE_OFFSET宏直接将kernel所用的虚拟地址转换成物理地址的区段。在文件mm/bootmem.c中对这个变量作初始化。在内存比较小的系统中max_pfn和max_low_pfn的值相同。 min_low_pfn, max_pfn和max_low_pfn这3个值,也要用于对高端内存(high memory)的起止位置的计算。在arch/i386/mm/init.c文件中会对类似的highstart_pfn和highend_pfn变量作初始化。这些变量用于对高端内存页面的分配。后面将描述。
等待队列的哈希表的分配和建立在free_area_init_core()函数中进行。哈希表的表项的数量在wait_table_size()函数中计算,并且保持在zone->wait_table_size成员中。最大4096个等待队列。最小是NoPages / PAGES_PER_WAITQUEUE的2次方,NoPages是zone管理的page的数量,PAGES_PER_WAITQUEUE被定义256。(原文:For smaller tables, the size of the table is the minimum power of 2 required to store NoPages / PAGES PER WAITQUEUE number of queues, where NoPages is the number of pages in the zone and PAGE PER WAITQUEUE is defined to be 256.) 下面这个公式可以用于计算这个值: