e gfp_t)___GFP_OTHER_NODE)
/* Room for N __GFP_FOO bits */
#define __GFP_BITS_SHIFT 26
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
给出的常数,其中一些很少使用,因此我不会讨论。其中最重要的一些常数语义如下所示
其中在开始的位置定义了对应的区修饰符, 定义在include/linux/gfp.h?v=4.7, line 46 ~ line 57
__GFP_DMA |
从ZONE_DMA中分配内存 |
__GFP_HIGHMEM |
从ZONE_HIGHMEM或ZONE_NORMAL中分配内存 |
__GFP_DMA32 |
从ZONE_DMA32中分配内存 |
__GFP_MOVABLE |
从__GFP_MOVABLE中分配内存 |
其次还定义了我们程序和函数中所需要的掩码MASK的信息, 由于其中__GFP_DMA, __GFP_DMA32, __GFP_HIGHMEM, __GFP_MOVABLE是在内存中分别有对应的内存域信息, 因此我们定义了内存域的掩码GFP_ZONEMASK, 参见include/linux/gfp.h?v=4.7, line 57
#define GFP_ZONEMASK (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
接着内核定义了行为修饰符
/* __GFP_WAIT表示分配内存的请求可以中断。也就是说,调度器在该请求期间可随意选择另一个过程执行,或者该请求可以被另一个更重要的事件中断. 分配器还可以在返回内存之前, 在队列上等待一个事件(相关进程会进入睡眠状态).
虽然名字相似,但__GFP_HIGH与__GFP_HIGHMEM毫无关系,请不要弄混这两者
__GFP_RECLAIMABLE __GFP_MOVABLE |
是页迁移机制所需的标志. 顾名思义,它们分别将分配的内存标记为可回收的或可移动的。这影响从空闲列表的哪个子表获取内存 |
__GFP_WRITE |
|
__GFP_HARDWALL |
只在NUMA系统上有意义. 它限制只在分配到当前进程的各个CPU所关联的结点分配内存。如果进程允许在所有CPU上运行(默认情况),该标志是无意义的。只有进程可以运行的CPU受限时,该标志才有效果 |
__GFP_THISNODE |
也只在NUMA系统上有意义。如果设置该比特位,则内存分配失败的情况下不允许使用其他结点作为备用,需要保证在当前结点或者明确指定的结点上成功分配内存 |
__GFP_ACCOUNT |
|
__GFP_ATOMIC |
|
__GFP_HIGH |
如果请求非常重要, 则设置__GFP_HIGH,即内核急切地需要内存时。在分配内存失败可能给内核带来严重后果时(比如威胁到系统稳定性或系统崩溃), 总是会使用该标志 |
__GFP_MEMALLOC |
|
__GFP_NOMEMALLOC |
|
__GFP_IO |
说明在查找空闲内存期间内核可以进行I/O操作. 实际上, 这意味着如果内核在内存分配期间换出页, 那么仅当设置该标志时, 才能将选择的页写入硬盘 |
__GFP_FS |
允许内核执行VFS操作. 在与VFS层有联系的内核子系统中必须禁用, 因为这可能引起循环递归调用. |
__GFP_DIRECT_RECLAIM |
|
__GFP_KSWAPD_RECLAIM |
|
__GFP_RECLAIM |
|
__GFP_REPEAT |
在分配失败后自动重试,但在尝试若干次之后会停止 |
__GFP_NOFAIL |
在分配失败后一直重试,直至成功 |
__GFP_NORETRY |
在分配失败后不重试,因此可能分配失败 |
__GFP_COLD |
如果需要分配不在CPU高速缓存中的“冷”页时,则设置__GFP_COLD |
__GFP_NOWARN |
在分配失败时禁止内核故障警告。在极少数场合该标志有用 |
__GFP_COMP |
添加混合页元素, 在hugetlb的代码内部使用 |
__GFP_ZERO |
在分配成功时,将返回填充字节0的页 |
__GFP_NOTRACK |
|
__GFP_NOTRACK_FALSE_POSITIVE __GFP_NOTRACK |
|
__GFP_OTHER_NODE |
|
那自然还有__GFP_BITS_SHIFT来表示我们所有的掩码位, 由于我们共计26个掩码位
/* Room for N __GFP_FOO bits */
#define __GFP_BITS_SHIFT 26
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
可以同时指定这些分配标志, 例如
ptr = kmalloc(size, __GFP_IO | __GFP_FS);
说明页分配器(最终会调用alloc_page)在分配时可以执行I/O, 在必要时还可以执行文件系统操作. 这就让内核有很大的自由度, 以便它尽可能找到空闲的内存来满足分配请求. 大多数分配器都会执行这些修饰符, 但一般不是这样直接指定, 而是将这些行为描述符标志进行分组, 即类型标志
3.3.4 掩码分组
最后来看第三部分, 由于这些标志几乎总是组合使用,内核作了一些分组,包含了用于各种标准情形的适当的标志. 称之为类型标志, 定义在include/linux/gfp.h?v=4.7, lien 194 ~ line 258
类型标志指定所需的行为和区描述符以安城特殊类型的处理, 正因为这一点, 内核总是趋于使用正确的类型标志, 而不是一味地指定它可能用到的多种描述符. 这么做既简单又不容易出错误.
如果有可能的话, 在内存管理子系统之外, 总是把下列分组之一用于内存分配. 在内核源代码中, 双下划线通常用于内部数据和定义. 而这些预定义的分组名没有双下划线前缀, 点从侧面验证了上述说法.
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO (__GFP_RECLAIM)
#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO)
#define GFP_TEMPORARY (__GFP_RECLAIM | __GFP_IO | __GFP_FS | \
__GFP_RECLAIMABLE)
#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
#define GFP_DMA __GFP_DMA
#define GFP_DMA32 __GFP_DMA32
#define GFP_HIGHUSER (GFP_USER