linux内核源码分析之伙伴系统

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之伙伴系统相关的知识,希望对你有一定的参考价值。

目录

内核映射

1,持久内核映射

查找地址 

解除映射

2,临时内存映射

3,没有高端内存


内核映射

尽管 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;
;
  • 建立pagevirtual的映射
  • 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_pkmaps
static 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内核源码分析之伙伴系统的主要内容,如果未能解决你的问题,请参考以下文章

linux内核源码分析之伙伴系统

linux内核源码分析之伙伴系统

内核源码解读之内存管理(10)percpu_page_set分析

linux内核源码分析之slab

linux内核源码分析之虚拟文件系统VFS

linux内核源码分析之proc文件系统