linux内核源码分析之伙伴系统
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之伙伴系统相关的知识,希望对你有一定的参考价值。
目录
内核映射
尽管 vmalloc 函数族可用于从高端内存域向内核映射页帧(这些在内核空间中通常是无法直接看 到的),但这并不是这些函数的实际用途。重要的是强调以下事实:内核提供了其他函数用于将 ZONE_HIGHMEM 页帧显式映射到内核空间,这些函数与 vmalloc 机制无关1,持久内核映射
如果需要将高端页帧长期映射(作为持久映射 )到内核地址空间中,必须使用 kmap 函数。需要映射的页用指向page 的指针指定,作为该函数的参数。 如果没有启用高端支持,该函数的任务就比较简单。在这种情况下,所有页都可以 直接访问 ,因此只需要返回页的地址,无需显式创建一个映射。 如果确实存在 高端页 ,情况会比较复杂。类似于vmalloc ,内核首先必须 建立高端页和所映射到的地址 之间的关联。还必须在虚拟地址空间中分配一个区域以映射页帧,最后,内核必须记录该虚拟区域的哪些部分在使用中,哪些仍然是空闲的。 数据结构:page_address_map 建立物理内存的page实例与其在虚拟内存区中位置之间的关联。struct page_address_map
struct page *page;
void *virtual;
struct list_head list;
;
- 建立page→virtual的映射
- page是一个指向全局mem_map数组中的page实例的指针;
- virtual指定了该页在内核虚拟地址空间中分配的位置。
查找地址
page_address首先检查传递进来的 page 实例在普通内存还是在高端内存。如果是前者,页地址可以根据page 在 mem_map 数组中的位置计算。对于后者,可通过上述散列表查找虚拟地址。创建映射
为通过 page 指针建立映射,必须使用 kmap 函数。 ① 它只是一个前端,用于确认指定的页是否确实 在高端内存域中。否则,结果返回 page_address 得到的地址。如果确实在高端内存中,则内核将工作委托给kmap_high ,该函数定义如下:void *kmap_high(struct page *page)
unsigned long vaddr;
lock_kmap();
vaddr = (unsigned long)page_address(page);
if (!vaddr)
vaddr = map_new_virtual(page);
pkmap_count[PKMAP_NR(vaddr)]++;
BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
unlock_kmap();
return (void*) vaddr;
- page_address函数首先检查该页是否已经映射。如果它不对应到有效地址,则必须使用map_new_virtual映射该页。
- 从最后使用的位置(保存在全局变量last_pkmap_nr中)开始,反向扫描pkmap_count数组,直至找到一个空闲位置。如果没有空闲位置,该函数进入睡眠状态,直至内核的另一部分执行解除映射操作腾出空位。
解除映射
用kmap 映射的页,如果不再需要,必须用 kunmap 解除映射 关键函数: flush_all_zero_pkmapsstatic void flush_all_zero_pkmaps(void)
int i;
int need_flush = 0;
flush_cache_kmaps();
for (i = 0; i < LAST_PKMAP; i++)
struct page *page;
if (pkmap_count[i] != 1)
continue;
pkmap_count[i] = 0;
BUG_ON(pte_none(pkmap_page_table[i]));
page = pte_page(pkmap_page_table[i]);
pte_clear(&init_mm, PKMAP_ADDR(i), &pkmap_page_table[i]);
set_page_address(page, NULL);
need_flush = 1;
if (need_flush)
flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
- flush_cache_kmaps在内核映射上执行刷出,因为内核的全局页表已经修改;
- 扫描整个pkmap_count数组。计数器值为1的项设置为0,从页表删除相关的项,最后删除该 映射;
- 最后,使用flush_tlb_kernel_range函数刷出所有与PKMAP区域相关的TLB项。
2,临时内存映射
kmap_atomic,其执行是原子的。该函数的一个主要优点是它比普通的kmap快速。但它不能用于可能进入睡眠的代码。因此,它对于很快就需要一个临时页的简短代码,是非常理想的。
void *kmap_atomic(struct page *page, enum km_type type)
3,没有高端内存
使用通用版本的kmap返回页的地址,且不修改虚拟内存。不能用于中断处理程序,如果pkmap数组中没有空闲位置,该函数会进入睡眠状态,直至情形有所改善。
void *kmap_high(struct page *page);
void kunmap_high(struct page *page);
static inline void *kmap(struct page *page)
BUG_ON(in_interrupt());
if (!PageHighMem(page))
return page_address(page);
return kmap_high(page);
static inline void kunmap(struct page *page)
BUG_ON(in_interrupt());
if (!PageHighMem(page))
return;
kunmap_high(page);
void *kmap_atomic(struct page *page);
void __kunmap_atomic(void *kvaddr);
参考
《深入Linux内核架构》
剖析Linux内核物理内存管理-大学生教程-腾讯课堂 (qq.com)
以上是关于linux内核源码分析之伙伴系统的主要内容,如果未能解决你的问题,请参考以下文章