目录
分配页面
1、分配物理页面的核心函数
struct page *alloc_pages(gfp_t gfp_mask ,unsigned int order);
unsigned long __get_free_pages(gfp_t gfp_mask,unsgined int order);
返回是内存的内核虚拟地址,如果所分配内存是线性映射的物理内存,则直接返回线性映射区的内核空间虚拟地址,不会使用高端内存。
2、分配一个页面
#define alloc_page(gfp_mask) alloc_pages(gfp_mask,0)
#define __get_free_page(gfp_mask) __get_free_pages(gfp_mask,0)
3、分配填充0的页面
unsigned long get_zeroed_page(gfp_t gfp_mask)
4、释放页面
void __free_pages(struct page *page,unsigned int order);
#define __free_page(page) ___free_pages((page),0)
#define free_page(addr) free_pages((addr),0)
5、分配掩码类型
- 内存管理区修饰符 __GFP_DMA __GFP_DMA32 __GFP_HIGHMEM GFP_MOVABLE
- 移动修饰符 __GFP_RECLAIMABLE,__GFP_HARDWALL
- 水位修饰符 __GFP_HIGH,__GFP_ATOMIC,GFP_MEMALLOC,GFP_NOMEMALLOC
- 页面回收修饰符 __GFP_IO,__GFP_FS ...
- 行为修饰符 __GFP_COLD,__GFP_NOWARN,__GFP_ZERO
组合使用
- GFP_KERNEL,常用,可能睡眠阻塞
- GFP_ATOMIC,保证不休眠并且分配成功
- GFP_NOWAIT,分配过程不允许睡眠
- GFP_NOFS 不会访问任何的文件系统的接口和操作
- GFP_USER 用户空间进程用来分配内存
- GFP_HIGHUSER ,用户空间 优先ZONE_HIGHNEN
- GFP_HIGHUSER_MOVABLE ,用户空间 优先ZONE_HIGHNEN,页面可迁移
- GFP_TRANSHUGE 透明页面分配
alloc_pages函数分析 ...
zonelist
每个内存节点在内核中抽象成一个zone,zone划分为多个区域,如ZONE_DMA,ZONE_DMA32,ZONE_NORMAL等。 在内核中使用zonelist数据结构管理一个内存节点zone
struct zonelist {
struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];
};
struct zoneref{
struct zone* zone;//指向实际的zone
int zone_idx;//编号,通常0表示最低的zone,如ZONE_DMA32,1表示ZONE_NORMAL
}
typedef struct palist_data{
struct zonelist node_zonelists[MAX_ZONELISTS];
}
- zonelist时所有可用zone的链表,排在第一个的zone是页面分配器首选的。
- 内存数据结构pglist_data中有两个zonelist。一个是ZONELIST_FALLBACK,指向本地zone;另外是ZONELIST_NOFALLBACK,用于NUMA系统,指向远端内存节点zone
- 建立zonelist 使用build_zonelist 如系统中有一个内存节点,有两个zone
- ZONE_NORMAL:_zonerefs[0]->zone_idx=1
- ZONE_DMA32:_zonerefs[1]->zone_idx=0 分配物理内存时,优先使用ZONE_NORMAL
first_zoneslist() 以GFP_KERNEL为例,gfp_zone(GFP_KERNEL)返回1,即highest_zoneidx=1,第一个zone是ZONE_NORMAL,其zone_idx=1 for_each_zonelist_nodemask() 会遍历ZONE_NORMAL和ZONE_DMA32