当内核分配内存时,需要指定gfp flag, 内核通过gfp标志判断从哪个zone分配内存。例如kmalloc(size_t size, gfp_t flags).
在linux中存在ZONE_DMA, ZONE_DMA32, ZONE_NORMAL, ZONE_HIGHMEM, ZONE_MOVABLE几个zone。
由于gfp_t 低4位,共有2^4=16中情况,而linux规定了低3位(DMA, DMA32, HIGHMEM)只能有1位为1.
gfp_t定义:
#define __GFP_DMA 0X01u
#define __GFP_HIGHMEM 0X02u
#define __GFP_DMA32 0X04u
#define __GFP_MOVABLE 0X08u
序号 | __GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32 | __GFP_MOVABLE | 组合结果 |
0 | 0 | 0 | 0 | 0 | 从ZONE_NORMAL分配 |
1 | 1 | 0 | 0 | 0 | 从ZONE_NORMAL或ZONE_DMA分配 |
2 | 0 | 1 | 0 | 0 | 从ZONE_HIGNMEM或ZONE_NORMAL分配 |
3 | 1 | 1 | 0 | 0 | BAD |
4 | 0 | 0 | 1 | 0 | 从ZONE_DMA32或ZONE_NORMAL分配 |
5 | 1 | 0 | 1 | 0 | BAD |
6 | 0 | 1 | 1 | 0 | BAD |
7 | 1 | 1 | 1 | 0 | BAD |
8 | 0 | 0 | 0 | 1 | 从ZONE_NORMAL分配 |
9 | 1 | 0 | 0 | 1 | 从ZONE_DMA或ZONE_NORMAL分配 |
a | 0 | 1 | 0 | 1 | 从ZONE_MOVABLE分配 Movable is valid only if HIGHMEM is set too |
b | 1 | 1 | 0 | 1 | BAD |
c | 0 | 0 | 1 | 1 | 从ZONE_DMA32分配 |
d | 1 | 0 | 1 | 1 | BAD |
e | 0 | 1 | 1 | 1 | BAD |
f | 1 | 1 | 1 | 1 | BAD |
根据上表,很容易得到GFP_ZONE_BAD,即上述BAD的bit或在一起。
#define GFP_ZONE_BAD ( \
1 << (___GFP_DMA | ___GFP_HIGHMEM) \
| 1 << (___GFP_DMA | ___GFP_DMA32) \
| 1 << (___GFP_DMA32 | ___GFP_HIGHMEM) \
| 1 << (___GFP_DMA | ___GFP_DMA32 | ___GFP_HIGHMEM) \
| 1 << (___GFP_MOVABLE | ___GFP_HIGHMEM | ___GFP_DMA) \
| 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA) \
| 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_HIGHMEM) \
| 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA | ___GFP_HIGHMEM) \
)
内核中定义ZONE_SHIFT,根据MAX_NR_ZONES,得到相应的ZONE_SHIFT。
#if MAX_NR_ZONES < 2
#define ZONES_SHIFT 0
#elif MAX_NR_ZONES <= 2
#define ZONES_SHIFT 1
#elif MAX_NR_ZONES <= 4
#define ZONES_SHIFT 2
#else
#error ZONES_SHIFT -- too many zones configured adjust calculation
#endif
将上表换一种形式表示,如下:
由于每种分配策略用ZONE_SHIFT位就可以表示,所以将上图中非BAD的位bitN,对应的分配策略,左移bitN * ZONE_SHIFT位,组合成GFP_ZONE_TABLE, 即:
#define GFP_ZONE_TABLE ( \
(ZONE_NORMAL << 0 * ZONES_SHIFT) \
| (OPT_ZONE_DMA << ___GFP_DMA * ZONES_SHIFT) \
| (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * ZONES_SHIFT) \
| (OPT_ZONE_DMA32 << ___GFP_DMA32 * ZONES_SHIFT) \
| (ZONE_NORMAL << ___GFP_MOVABLE * ZONES_SHIFT) \
| (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * ZONES_SHIFT) \
| (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * ZONES_SHIFT) \
| (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * ZONES_SHIFT) \
)
另外还定义GFP_ZONEMASK:
#define GFP_ZONEMASK (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
通过GFP_ZONE_TABLE宏,给定gfp的低4位,就可以找到分配内存的zone,函数如下:
static inline enum zone_type gfp_zone(gfp_t flags)
{
enum zone_type z;
int bit = (__force int) (flags & GFP_ZONEMASK);
z = (GFP_ZONE_TABLE >> (bit * ZONES_SHIFT)) &
((1 << ZONES_SHIFT) - 1);
VM_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
return z;
}