日期:2014-05-16  浏览次数:20727 次

linux内存伙伴算法(三:分配页)

伙伴系统中用于分配页的函数如下:

alloc_pages(mask,order)分配2^order页并返回一个struct page的实例,表示分配的内存块的起始页。alloc_page(mask)是前者在order=0情况下的简化形式,只分配一页。

get_zeroed_page(mask)分配一页并返回一个page实例,页对应的内存填充0(所有其他函数分配之后的内容是未定义的)。

__get_free_pages(mask,order)和__get_free_page(mask)的工作方式与上述函数相同,但返回分配内存块的虚拟地址,而不是page实例。

get_dma_pages(gfp_mask,order)用来获得适用于DMA的页。

在空闲内存无法满足请求以至于分配失败的情况下,所有上述函数都返回空指针(alloc_pages和alloc_page)或者0(get_zeroed_page、__get_free_pages和__get_free_page)。因此内核在各次分配之后必须检查返回的结果。这种惯例与设计得很好的用户层应用程序没有什么不同,但在内核中忽略检查将会导致严重得多的故障。

前述所有函数中使用的mask参数的语义是什么?linux将内核划分为内存域,内核提供了所谓的内存域修饰符,来指定从哪个内存域分配所需的页。

#define __GFP_DMA	((__force gfp_t)0x01u)
#define __GFP_HIGHMEM	((__force gfp_t)0x02u)
#define __GFP_DMA32	((__force gfp_t)0x04u)
除了内存域修饰符之外,掩码中还可以设置一些标志,这些额外的标志并不限制从哪个物理内存段分配内存,但确实可以改变分配器的行为。

#define __GFP_WAIT	((__force gfp_t)0x10u)	//表示分配内存的请求可以中断。也就是说,调度器在该请求期间可随意选择另一个过程执行,或者该请求可以被另一个更重要的事件中断。
#define __GFP_HIGH	((__force gfp_t)0x20u)	//如果请求非常重要,则设置__GFP_HIGH,即内核急切的需要内存时。在分配内存失败可能给内核带来严重得后果时,一般会设置该标志
#define __GFP_IO	((__force gfp_t)0x40u)	//在查找空闲内存期间内核可以进行I/O操作。这意味着如果内核在内存分配期间换出页,那么仅当设置该标志时,才能将选择的页写入磁盘。
#define __GFP_FS	((__force gfp_t)0x80u)	//允许内核执行VFS操作
#define __GFP_COLD	((__force gfp_t)0x100u)	//如果需要分配不在CPU高速缓存中的“冷”页时,则设置__GFP_COLD。
#define __GFP_NOWARN	((__force gfp_t)0x200u)	//在分配失败时禁止内核故障警告。
#define __GFP_REPEAT	((__force gfp_t)0x400u)	//在分配失败后自动重试,但在尝试若干次之后会停止。
#define __GFP_NOFAIL	((__force gfp_t)0x800u)	//在分配失败后一直重试,直至成功。
#define __GFP_NORETRY	((__force gfp_t)0x1000u)//不重试,可能失败
#define __GFP_COMP	((__force gfp_t)0x4000u)//增加复合页元数据
#define __GFP_ZERO	((__force gfp_t)0x8000u)//在分配成功时,将返回填充字节0的页。
#define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) //不适用紧急分配链表
#define __GFP_HARDWALL   ((__force gfp_t)0x20000u) //只在NUMA系统上有意义。它限制只在分配到当前进程的各个CPU所关联的结点分配内存。如果进程允许在所有的CPU上运行(默认情况下),该标志是没有意义的。只有进程可以运行的CPU受限时,该标志才有意义。
#define __GFP_THISNODE	((__force gfp_t)0x40000u)//页只在NUMA系统上有意义,如果设置该比特位,则内存分配失败的情况下不允许使用其他结点作为备用,需要保证在当前结点或者明确指定的结点上成功分配内存。
#define __GFP_RECLAIMABLE ((__force gfp_t)0x80000u) //将分配的内存标记为可回收
#define __GFP_MOVABLE	((__force gfp_t)0x100000u)  //将分配的内存标记为可移动

#define __GFP_BITS_SHIFT 21	/* Room for 21 __GFP_FOO bits */
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))

/* This equals 0, but use constants in case they ever change */
#define GFP_NOWAIT	(GFP_ATOMIC & ~__GFP_HIGH)
由于这些标志总是组合使用,内核做了一些分组,包含了用于各种标准情形的适当地标志。

#define GFP_ATOMIC	(__GFP_HIGH)//用于原子分配,在任何情况下都不能中断,可能使用紧急分配链表中的内存
#define GFP_NOIO	(__GFP_WAIT)//明确禁止IO操作,但可以被中断
#define GFP_NOFS	(__GFP_WAIT | __GFP_IO)//明确禁止访问VFS层操作,但可以被中断
#define GFP_KERNEL	(__GFP_WAIT | __GFP_IO | __GFP_FS)//内核分配的默认配置
#define GFP_TEMPORARY	(__GFP_WAIT | __GFP_IO | __GFP_FS | \
			 __GFP_RECLAIMABLE)
#define GFP_USER	(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)//用户分配的默认配置
#define GFP_HIGHUSER	(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \
			 __GFP_HIGHMEM)//是GFP_USER的一个扩展,页用于用户空间,它允许分配无法直接映射的高端内存,使用高端内存页是没有坏处的,因为用户过程的地址空间总是通过非线性页表组织的
#define GFP_HIGHUSER_MOVABLE	(__GFP_WAIT | __GFP_IO | __GFP_FS | \
				 __GFP_HARDWALL | __GFP_HIGHMEM | \
				 __GFP_MOVABLE)//类似于GFP_HIGHUSER,但分配是在虚拟内存域ZONE_MOVABLE中进行
#define GFP_NOFS_PAGECACHE	(__GFP_WAIT | __GFP_IO | __GFP_MOVABLE)
#define GFP_USER_PAGECACHE	(__GFP_WAIT | __GFP_IO | __GFP_FS | \
				 __GFP_HARDWALL | __GFP_MOVABLE)
#define GFP_HIGHUSER_PAGECACHE	(__GFP_WAIT | __GFP_IO | __GFP_FS | \
				 __GFP_HARDWALL | __GFP_HIGHMEM | \
				 __GFP_MOVABLE)

#ifdef CONFIG_NUMA
#define GFP_THISNODE	(__GFP_THI