Linux的内存管理,实际上是借助80x86的硬件分段和分页电路,将逻辑地址转化为物理地址的。
物理内存中,有一部分是一直(Permanently)映射给内核使用的,这部分主要用于保存内核的代码,以及内核中静态的数据结构体。之所以要一直将这些物理内存映射给内核,是因为这些内容(代码,静态数据结构)是在整个操作系统运行过程中都一直需要不断地引用的,如果是通过动态分配和翻译的方式来维护它们在物理内存中的位置的话,就会耗费太多的CPU时间。
这种方式可以理解为以空间换时间的策略。
物理内存中的其余部分,是动态内存。
动态内存是一项珍贵的资源,不仅被用户态的各个进程所需要,内核本身也是需要的。
Page Frame Management
Memory Area Management
是两种管理物理上连接的内存区域的方式。
Noncontiguous Memory Area Management
是处理物理上不连接的物理内存区域的方式。
Page Descriptor
内核必须维护每一个Page的当前状态,
比如,它必须能够区分某个物理页现在是被谁在使用:
1. 用户态的进程
2. 内核态的代码
3. 内核态的数据结构
同样,它也必须能够区分一个在Dynamic Memory中分配的物理内存页现在处于哪种状态:
1. 释放状态
2. 存储用户态的进程的数据
3. 存储一个软件的Cache
4. 存储动态分配的内核的数据结构
5. 存储设备驱动的缓存数据
6. 存储内核模块的代码
每个页的描述结构体,都存储成一个struct page的实例,这些实例保存在mem_map数组中。
每个struct page大小为32字节,因此大约会耗费1%(32 / 4096)的物理内存来保存这个数组。
内核提供以下几个宏,来获得page结构体的位置:
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
2:
/*
* supports 3 memory models.
*/
#if defined(CONFIG_FLATMEM)
7:
#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET))
long)((page) - mem_map) + \
10: ARCH_PFN_OFFSET)
#elif defined(CONFIG_DISCONTIGMEM)
12:
#define __pfn_to_page(pfn) \
long __pfn = (pfn); \
long __nid = arch_pfn_to_nid(__pfn); \
16: NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\
17: })
18:
#define __page_to_pfn(pg) \
struct page *__pg = (pg); \
struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \
long)(__pg - __pgdat->node_mem_map) + \
23: __pgdat->node_start_pfn; \
24: })
25:
#elif defined(CONFIG_SPARSEMEM_VMEMMAP)
27:
/* memmap is virtually contiguous. */
#define __pfn_to_page(pfn) (vmemmap + (pfn))
long)((page) - vmemmap)
31:
#elif defined(CONFIG_SPARSEMEM)
/*
* Note: section's mem_map is encorded to reflect its start_pfn.
* section[i].section_mem_map == mem_map's address - start_pfn;
*/
#define __page_to_pfn(pg) \
struct page *__pg = (pg); \
int __sec = page_to_section(__pg); \
long)(__pg - __section_mem_map_addr(__nr_to_section(__sec))); \
41: })
42:
#define __pfn_to_page(pfn) \
long __pfn = (pfn); \
struct mem_section *__sec = __pfn_to_section(__pfn); \
46: __section_mem_map_addr(__sec) + __pfn; \
47: })
/* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */
49:
#define page_to_pfn __page_to_pfn
#define pfn_to_page __pfn_to_page