2.4 物理页面的分配

本文详细介绍了Linux内核中的页面分配和释放机制,包括gfp_mask标志位的作用、页面分配函数如alloc_pages的工作原理,以及如何基于zone进行内存分配。同时,文章探讨了gfp_mask与zone及迁移类型的关系,并解释了zonewatermark的概念。此外,还讲解了per-CPU页面分配的高效机制。

Table of Contents

页面分配和释放函数

gfp_mask标志位

gfp_mask和zone以及迁移类型之间的关系

zone watermark

per-CPU页面分配


页面分配和释放函数

页面核心分配函数:

#define alloc_pages(gfp_mask, order) \                   

__________alloc_pages_node(numa_node_id(), gfp_mask, order)

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);/* 返回线性地址 */

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)        
{
____struct page *page;

____/*
____ * __get_free_pages() returns a 32-bit address, which cannot represent
____ * a highmem page
____ */
____VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);

____page = alloc_pages(gfp_mask, order);
____if (!page)
________return 0;
____return (unsigned long) page_address(page);
}

对应的释放内存的函数:

void __free_pages(struct page *page, unsigned int order)

void free_pages(unsigned long addr, unsigned int order)

gfp_mask标志位

表示内存分配的action;表示内存分配的zone;表示从哪些迁移类型的page block中分配内存。

/*
 * Action modifiers - doesn't change the zoning
 *
 * __GFP_REPEAT: Try hard to allocate the memory, but the allocation attempt
 * _might_ fail.  This depends upon the particular VM implementation.
 *
 * __GFP_NOFAIL: The VM implementation _must_ retry infinitely: the caller
 * cannot handle allocation failures.  This modifier is deprecated and no new
 * users should be added.                                                    
 *
 * __GFP_NORETRY: The VM implementation must not retry indefinitely.
 *
 * __GFP_MOVABLE: Flag that this page will be movable by the page migration
 * mechanism or reclaimed
 */

#define __GFP_WAIT__((__force gfp_t)___GFP_WAIT)____/* Can wait and reschedule? */
#define __GFP_HIGH__((__force gfp_t)___GFP_HIGH)____/* Should access emergency pools? */
#define __GFP_IO____((__force gfp_t)___GFP_IO)__/* Can start physical IO? */
#define __GFP_FS____((__force gfp_t)___GFP_FS)__/* Can call down to low-level FS? */
#define __GFP_COLD__((__force gfp_t)___GFP_COLD)____/* Cache-cold page required */
#define __GFP_NOWARN____((__force gfp_t)___GFP_NOWARN)__/* Suppress page allocation failure warning */
#define __GFP_REPEAT____((__force gfp_t)___GFP_REPEAT)__/* See above */
#define __GFP_NOFAIL____((__force gfp_t)___GFP_NOFAIL)__/* See above */                               
#define __GFP_NORETRY___((__force gfp_t)___GFP_NORETRY) /* See above */
#define __GFP_MEMALLOC__((__force gfp_t)___GFP_MEMALLOC)/* Allow access to emergency reserves */
#define __GFP_COMP__((__force gfp_t)___GFP_COMP)____/* Add compound page metadata */
#define __GFP_ZERO__((__force gfp_t)___GFP_ZERO)____/* Return zeroed page on success */
#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves.

......

/*
 * GFP bitmasks..
 *
 * Zone modifiers (see linux/mmzone.h - low three bits)
 *
 * Do not put any conditional on these. If necessary modify the definitions
 * without the underscores and use them consistently. The definitions here may
 * be used in bit comparisons.
 */
#define __GFP_DMA___((__force gfp_t)___GFP_DMA)
#define __GFP_HIGHMEM___((__force gfp_t)___GFP_HIGHMEM)
#define __GFP_DMA32_((__force gfp_t)___GFP_DMA32)
#define __GFP_MOVABLE___((__force gfp_t)___GFP_MOVABLE)  /* Page is movable */
#define GFP_ZONEMASK____(__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)  

#define GFP_ATOMIC__(__GFP_HIGH)
#define GFP_NOIO____(__GFP_WAIT)
#define GFP_NOFS____(__GFP_WAIT | __GFP_IO)
#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_USER | __GFP_HIGHMEM)
#define GFP_HIGHUSER_MOVABLE____(GFP_HIGHUSER | __GFP_MOVABLE)
#define GFP_IOFS____(__GFP_IO | __GFP_FS)
#define GFP_TRANSHUGE___(GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
____________ __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN | \
____________ __GFP_NO_KSWAPD)

GFP_ATOMIC:分配过程中不允许休眠,通常用在中断处理程序,下半部、持有自旋锁等不能睡眠的地方。

GFP_KERNEL:常规的内存分配方式,可以睡眠。

GFP_USER:常用语用户空间分配内存。

GFP_HIGHUSER:在ZONE_HIGHMEM开始进行分配,也是常用于用户进程分配内存

GFP_NOIO:分配可以阻塞,但不会启动磁盘I/O。

GFP_NOFS:分配可以阻塞,可以启动磁盘,但不会启动文件系统操作。

gfp_mask和zone以及迁移类型之间的关系

页面分配器是基于zone来设计的,因此,通过gfp_mask可以确认页面的分配来自哪些zone

__alloc_pages()->alloc_pages_nodemask()->get_page_from_freelist()->

or_each_zone_zonelist_nodemask(zone, z, zonelist, ac->high_zoneidx,
___________________________ac->nodemask) {......}

系统优先使用ZONE_HIGHMEM,然后是ZONE_NORMAL。

struct zonelist {
____struct zonelist_cache *zlcache_ptr;_____     // NULL or &zlcache
____struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];           
#ifdef CONFIG_NUMA
____struct zonelist_cache zlcache;__________     // optional ...
#endif
};

/*
 * This struct contains information about a zone in a zonelist. It is stored
 * here to avoid dereferences into large structures and lookups of tables
 */
struct zoneref {                                     
____struct zone *zone;__/* Pointer to actual zone */
____int zone_idx;_______/* zone_idx(zoneref->zone) */
};

页面分配的时候也需要知道当前分配是要从哪个迁移类型中分配内存:

alloc_pages_nodemask()->gfpflags_to_migratetype(gfp_mask)

/*
 * This is the 'heart' of the zoned buddy allocator.
 */
struct page *
__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
____________struct zonelist *zonelist, nodemask_t *nodemask)
{
____struct zoneref *preferred_zoneref;
____struct page *page = NULL;
____unsigned int cpuset_mems_cookie;
____int alloc_flags = ALLOC_WMARK_LOW|ALLOC_CPUSET|ALLOC_FAIR;
____gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
____struct alloc_context ac = {
________.high_zoneidx = gfp_zone(gfp_mask),
________.nodemask = nodemask,
________.migratetype = gfpflags_to_migratetype(gfp_mask),                  
____};
.......
}


/* Convert GFP flags to their corresponding migrate type */
static inline int gfpflags_to_migratetype(const gfp_t gfp_flags)
{
____WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);

____if (unlikely(page_group_by_mobility_disabled))
________return MIGRATE_UNMOVABLE;                               

____/* Group based on mobility */
____return (((gfp_flags & __GFP_MOVABLE) != 0) << 1) |
________((gfp_flags & __GFP_RECLAIMABLE) != 0);
}

zone watermark

enum zone_watermarks {
____WMARK_MIN,
____WMARK_LOW,
____WMARK_HIGH,                                       
____NR_WMARK
};

#define min_wmark_pages(z) (z->watermark[WMARK_MIN])
#define low_wmark_pages(z) (z->watermark[WMARK_LOW])
#define high_wmark_pages(z) (z->watermark[WMARK_HIGH])

per-CPU页面分配

内核进程请求和释放单个页面。

页面分配器分配和释放页面的时候需要申请一把锁:zone->lock。

为了提高单个页框的分配效率,内核建立了per_cpu页面高速缓存池,其中存放了若干预先分配的页框。

当请求单个页面时,直接从本地cpu的页框高速缓存池中获取页框。

struct zone {
......
____struct pglist_data__*zone_pgdat;
____struct per_cpu_pageset __percpu *pageset;
......
____/* Write-intensive fields used from the page allocator */
____spinlock_t______lock;
......
}

struct per_cpu_pageset {                    
____struct per_cpu_pages pcp;
......
};

struct per_cpu_pages {                                                 
____int count;______/* number of pages in the list */
____int high;_______/* high watermark, emptying needed */
____int batch;______/* chunk size for buddy add/remove */

____/* Lists of pages, one per migrate type stored on the pcp-lists */
____struct list_head lists[MIGRATE_PCPTYPES];
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值