对于 64 位系统,有两个 DMA 区域 。除了上面说的 ZONE_DMA,还有 ZONE_DMA32 。在这里你大概能理解 DMA 的原理就可以,不必纠结,我们后面会讲 DMA 的机制 。
ZONE_NORMAL 是直接映射区,就是上一节讲的,从物理内存到虚拟内存的内核区域,通过加上一个常量直接映射 。
ZONE_HIGHMEM 是高端内存区,就是上一节讲的,对于 32 位系统来说超过 896M 的地方,对于 64 未必要有的一段区域 。
ZONE_MOVABLE 是可移动区域,通过将物理内存划分为可移动分配区域和不可移动分配区域来避免内存碎片 。
这里你需要注意一下,我们刚才对于区域的划分,都是针对物理内存的 。
nr_zones 表示当前节点的区域的数量 。node_zonelists 是备用节点和它的内存区域的情况 。前面讲 NUMA 的时候,我们讲了 CPU 访问内存,本节点速度最快,但是如果本节点内存不够怎么办,还是需要去其他节点进行分配 。毕竟,就算在备用节点里面选择,慢了点也比没有强 。
既然整个内存被分成了多个节点,那 pglist_data 应该放在一个数组里面 。每个节点一项,就像下面代码里面一样:
struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;区域到这里,我们把内存分成了节点,把节点分成了区域 。接下来我们来看,一个区域里面是如何组织的 。
表示区域的数据结构 zone 的定义如下:
struct zone {...... struct pglist_data *zone_pgdat; struct per_cpu_pageset __percpu *pageset; unsigned longzone_start_pfn; /** spanned_pages is the total pages spanned by the zone, including* holes, which is calculated as:*spanned_pages = zone_end_pfn - zone_start_pfn;** present_pages is physical pages existing within the zone, which* is calculated as:* present_pages = spanned_pages - absent_pages(pages in holes);** managed_pages is present pages managed by the buddy system, which* is calculated as (reserved_pages includes pages allocated by the* bootmem allocator):* managed_pages = present_pages - reserved_pages;**/ unsigned longmanaged_pages; unsigned longspanned_pages; unsigned longpresent_pages; const char*name;...... /* free areas of different sizes */ struct free_area free_area[MAX_ORDER]; /* zone flags, see below */ unsigned longflags; /* Primarily protects free_area */ spinlock_tlock;......} ____cacheline_internodealigned_in_在一个 zone 里面,zone_start_pfn 表示属于这个 zone 的第一个页 。
如果我们仔细看代码的注释,可以看到,spanned_pages = zone_end_pfn - zone_start_pfn,也即 spanned_pages 指的是不管中间有没有物理内存空洞,反正就是最后的页号减去起始的页号 。
present_pages = spanned_pages - absent_pages(pages in holes),也即 present_pages 是这个 zone 在物理内存中真实存在的所有 page 数目 。
managed_pages = present_pages - reserved_pages,也即 managed_pages 是这个 zone 被伙伴系统管理的所有的 page 数目,伙伴系统的工作机制我们后面会讲 。
per_cpu_pageset 用于区分冷热页 。什么叫冷热页呢?咱们讲 x86 体系结构的时候讲过,为了让 CPU 快速访问段描述符,在 CPU 里面有段描述符缓存 。CPU 访问这个缓存的速度比内存快得多 。同样对于页面来讲,也是这样的 。如果一个页被加载到 CPU 高速缓存里面,这就是一个热页(Hot Page),CPU 读起来速度会快很多,如果没有就是冷页(Cold Page) 。由于每个 CPU 都有自己的高速缓存,因而 per_cpu_pageset 也是每个 CPU 一个 。
页了解了区域 zone,接下来我们就到了组成物理内存的基本单位,页的数据结构 struct page 。这是一个特别复杂的结构,里面有很多的 union,union 结构是在 C 语言中被用于同一块内存根据情况保存不同类型数据的一种方式 。这里之所以用了 union,是因为一个物理页面使用模式有多种 。
**第一种模式,要用就用一整页 。**这一整页的内存,或者直接和虚拟地址空间建立映射关系,我们把这种称为匿名页(Anonymous Page) 。或者用于关联一个文件,然后再和虚拟地址空间建立映射关系,这样的文件,我们称为内存映射文件(Memory-mApped File) 。
如果某一页是这种使用模式,则会使用 union 中的以下变量:
- struct address_space *mapping 就是用于内存映射,如果是匿名页,最低位为 1;如果是映射文件,最低位为 0;
- pgoff_t index 是在映射区的偏移量;
- atomic_t _mapcount,每个进程都有自己的页表,这里指有多少个页表项指向了这个页;
- struct list_head lru 表示这一页应该在一个链表上,例如这个页面被换出,就在换出页的链表中;
- PPPoE报文交互详解
- 菜鸡程序员的一天都在折腾些什么?
- 滇红中国红茶叶,滇红茶叶的历史
- 越南酒店里的红茶,红茶的发展历史和状况
- 一个基于Vue3的无编译小框架 byview
- 英国人喝红茶的历史,锡兰茶的名与他的什么特点
- 功夫红茶得历史,功夫红茶历史
- 17 个高频好用的JavaScript 代码块
- 斯里兰卡比较好的红茶,斯里兰卡红茶历史
- 红茶茶艺表演解说,宣州名优茶的历史与特点
