页
- 页表查询的过程由硬件完成,但是页表的维护需要软件完成,处理器的MMU单元负责把虚拟地址转换成物理地址
- 当处理器发现虚拟地址无法通过页表映射到对应的物理地址时,就会触发一个缺页异常,挂起出错的进程,操作系统软件需要处理这个缺页异常。
- 一个页表可以由页缓存使用,可以作为私有数据,也可以作为进程页表中的映射
- page结构是和物理页相关联的,而不是和虚拟页相关联的。内核用这一数据结构来知道一个页是否空闲,如果页被分配的话,谁拥有这个页,是用户空间进程、动态分配的内核数据还是静态内核代码或页高速缓存。
区
- 由于硬件限制,内核并不能对所有的页一视同仁。有些页位域内存中特定的物理地址上,所以不能将其用于一些特定的任务。由于存在这种限制,所以内核把页划分为不同的区(Zone)。这样就可以根据用途进行分配内存,例如ZONE_DMA内存池让内核有能力为MDA分配所需的内存。
- 区的划分没有任何物理意义,只不过是内核为了管理页而采用的一种逻辑上的分组。某些分配可能需要从特定的区中获取页,而另外的一些分配则可以从多个区中获取页。例如一般用途的内存既能从ZONE_DMA分配,也能从ZONE_NORMAL分配,不过不能同时从两个区分配,因为分配是不能跨区的。
获得页
- struct page* alloc_pages(gfp_t gfp_mask, unsigned int order) 该函数分配2^order个连续的物理页,返回一个指针,指向第一个页的page结构体。如果申请失败返回NULL。
- void* page_address(struct page* page) 该函数把一个给定的页返回它的逻辑地址。
- unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) 这个函数与alloc_pages()作用相同,不过它直接返回所请求的第一个页的逻辑地址。
- void __free_pages(struct page* page, unsigned int order) 释放页
- void free_pages(unsigned long addr, unsigned int order) 释放页
kmalloc()
- void* kmalloc(size_t size, gfp_t flags) 这个函数返回一个指向内存块的指针,其内存块至少有size大小。
- void* kfree(const void* ptr) 这个函数释放由kmalloc()分配出来的内存块。
gpf_mask标志
- 行为修饰符:行为修饰符标识内核应当如何分配所需的内存,在某些情况下,只能使用某些特定的方法分配内存。例如,中断处理程序就要求内核在分配内存的过程中不能睡眠。
- 区修饰符:去修饰符表示从哪儿分配内存。
- 类型:类型标志组合了行为修饰符和区修饰符,将各种可能用到的类型归纳为不同的类型,简化了修饰符的使用。
vmalloc()
- void* vmalloc(unsigned long size) 这个函数与kmalloc()相比,分配的内存虚拟地址是连续的,物理地址则无需连续。而kmalloc()分配的内存在虚拟地址和物理地址上都是连续的。
- void* vfree(const void* addr) 这个函数释放由vmalloc()分配出来的内存块。
Slab
- Slab分配器在内核中扮演者通用数据结构缓存层的角色。Slab分配器试图在几个基本原则之间寻求一种平衡:
- 频繁使用的数据结构也会频繁分配和释放,因此应当缓存他们。
- 频繁分配和回收必然会导致内存碎片,为了避免这种表象,空闲链表的缓存会连续地存放。
- 回收的对象可以立即投入下一次分配,因此,对于频繁的分配和释放,空闲链表能够提高其性能。
- 如果分配器知道对象大小、页大小和总的高速缓存的大小这样的概念,它会做出更明智的决策。
- 如果让部分缓存专属于单个处理器,那么分配和释放就可以在不加锁的情况下进行。
- 如果分配器与NUMA相关的,它就可以从相同的内存节点为请求者分配。
- 对存放的对象进行着色,以防止多个对象映射到相同的高速缓存行
- Slab分配器把不同对对象划分为所谓的高速缓存组,每个高速缓存组存放不同类型的对象,每种对象类型对应一个高速缓存。高速缓存又被划分为slab,slab由一个或多个物理上连续的页组成。
栈上静态分配内存
- 通常,内核空间里的每个进程都有一个页或者两个页的栈空间。
- 中断栈为每个进程提供一个用于中断处理程序的栈,有了这个选项,中断处理程序不用再和被中断进程共享一个内核栈。
高端内存的映射
- 高端内存中的页不能永久地映射到内核的地址空间上。因此,从高端内存获得的页不能有逻辑地址。
- void* kmap(struct page* page) 这个函数返回该页对应的虚拟地址。如果这个页来自于低端内存,则单纯返回虚拟地址,如果这个页来自于高端内存,则先建立一个永久映射,再返回地址。
- void* kunmap(struct page* page) 这个函数解除建立的永久映射。
- void* kmap_atomic(struct page* page, enum km_type type) 这个函数建立一个临时映射,该处理为原子操作,不会被阻塞。
- void* kunmap_atomic(void* kvaddr, enum km_type type) 这个函数解除建立的临时映射。