1.地址类型
用户虚拟地址
物理地址
总线地址
内核逻辑地址 kmalloc()分配的内存区域
内核虚拟地址 vmalloc()分配的内存区域
在<asm/page.h>中,宏__pa()/__va()可以进行逻辑地址和物理地址之间的转换。但只对低端内存页有效。
2.物理地址和页
PAGE_SIZE PAGE_SHIFT
3.高端与低端内存
有多少内存可以直接映射到逻辑地址是受限制的。
只有内存的低端部分(依赖于硬件和呵呵的设置,一般为1到2GB)拥有逻辑地址;
剩余的部分(高端内存)没有逻辑地址,在访问特定的高端内存页前,内核必须建立明确的虚拟映射,使该
页可在内核地址空间中被访问。因此,许多内核数据结构必须被设置在低端内存中,而高端内存趋于为用户
空间进程页所保留。
4.内存映射和页结构
page结构中有几个重要成员
atomic_t count; 引用计数;
void *virtual 如果页面被映射,则指向页的内核虚拟地址;
unsigned long flags; 描述页状态的一系列标志。
一些宏和函数
struct page *virt t_to_page(void *kaddr)
将内核逻辑地址转换为page结构指针,因为需要逻辑地址,不能用于vmalloc()分配的地址和高端内存
struct page *pfn_to_page(int pfn)
通过给定的页帧号返回page指针
struct page *page_address(struct page *page)
返回页的内核虚拟地址,不可以用于未映射的高端内存
void *kmap(struct page *page)
返回页的虚拟地址,可以用于未映射的高端内存
void kunmap(struct page *page)
5页表
页表是将虚拟地址转换为相应的物理地址的一种机制。
6.虚拟内存区
进程的内存映射至少包含:程序的可执行代码区域,多个数据区域,与每个活动的内存映射对应的区域
下面给出了vm_area_struct的几个重要成员:
struct vm_area_struct {
struct mm_struct; unsigned long vm_start; unsigned long vm_end; pgprot_t vm_page_prot; /* Access permissions of this VMA. */ unsigned long vm_flags; //我们感兴趣的标志是VM_IO VM_RESERVED
struct vm_operations_struct * vm_ops;
unsigned long vm_pgoff; //该区域在文件中的偏移量 struct file * vm_file; //指向与该区域相关联的file结构指针 void * vm_private_data; //保存自身信息的成员
};
给出了vm_operations_struct的重要成员
struct vm_operations_struct {
void (*open) (...);
void (*close) (...);
struct page *(*nopage)(...) //将物理页从辅助存储器中读入后,返回指向物理页的page结构指针
}
7.内存映射处理
在系统中的每一个进程都拥有一个struct mm_struct 结构,其中包含了虚拟内存区域链表,页表以及其他大量
内存管理信息。
8.mmap设备操作
不是所有的设备都能进行mmap抽象的。比如像串口和其他面向流的设备就不能做这样的抽象。
mmap的另一个限制是必须以PAGE_SIZE为单位进行映射。内核只能在页表一级上对虚拟地址进行管理,因此那
些被映射的区域必须是PAGE_SIZE的整数倍。
执行mmap,驱动主要完成两件工作:
为该地址范围建立合适的页表;
将vma->vm_ops替换为一系列的新操作
两种建立页表的方法:
8.1 remap_pfn_range函数一次全部建立
int remap_pfn_range(struct vm_area_struct *vma,unsigned long virt_addr,unsigned long pfn,
unsigned long size,pgprot_t prot);
8.2 nopage VMA方法每次建立一个页表,nopage函数需要为失效地址查找正确的page结构,并且增加它的引用计数。
strut page *(*nopage)(struct vm_area_struct *vma,unsigned long address,int *type);
pfn_valid函数可以判断页帧号是否合法
在设备驱动中,type的正确值应该总是VM_FAULT_MINOR
返回值为一个指向page结构的指针,出错将返回NOPAGE_SIGBUS,返回NOPAGE_OOM表示资源紧张而造成的错误。
PCI内存被映射到系统内存最高端上,因此在系统内存映射中没有这些地址的入口,无法返回一个指向page的指针,所以必须使用
remap_pfn_range
如果一个进程调用mremap扩充一个映射区域,而驱动程序没有实现nopage,则进程将最终得到一块全是零的内存,而不会产生段
故障错误
9.重新映射RAM
remap_page_range只能访问保留页和超出物理内存的物理地址。在linux中,内存映射时,物理地址页被标记为“保留的”,表示内存管理
对其不起作用
remap_page_range不允许重新映射常规地址,包括调用get_free_page函数所获得的地址。但是他能重新映射高端PCI缓冲区和ISA内存和
零内存页。
10.nopage重新映射RAM,重新映射内核虚拟地址
在scullp中 virt_to_page获得相应的page结构指针 (该函数不能在内核虚拟空间使用)。
在scullv中 vmalloc_to_page获得相应的 page结构指针