您的位置:首页 > 运维架构 > Linux

Linux内核之内存管理

2016-08-10 19:33 323 查看

一、页page

内核把物理页作为内存管理的基本单元。内存管理单元(MMU)通常以页为单位进行处理。从虚拟内存的角度看,页就是最小单位。

32位体系结构支持4KB的页,而64位的体系结构支持8KB的页。

内核用struct page结构来表示系统中的每个物理页。

二、区zone

由于硬件的限制,内核就把所有的页划分成不同的区(zone),Linux主要有四种分区:

ZONE_DMA–该区包含的页能用来执行DMA操作

ZONE_DMA32–也可用来执行DMA操作,但是只能被32位设备访问

ZONE_NARMAL–该区包含的是能正常映射的页

ZONE_HIGHMEM–高端内存,并不能永久映射到内核地址空间

x86-32上,分区情况:

描述物理内存
ZONE_DMADMA使用的页<16M
ZONE_NORMAL正常可寻址的页16~896M
ZONE_HIGHMEM动态映射的页大于896M

三、获得页

static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)


该函数分配2的order次方个连续的物理页,返回指向第一个页的结构体的指针。

void *page_address(const struct page *page)


将指定的页转换成它的逻辑地址。

若无需用到struct page,就可以调用:

extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
//注意,该函数要进行错误检查,分配失败必须进行相应处理


该函数直接返回请求的第一个页的逻辑地址。

如果只需要一页,就可以用一下两个函数:

struct page * alloc_page(gdp_t gfp_mask)
unsigned long __get_free_page(gfp_t gfp_mask);


如果你需要让返回的页的内容全为0,请用下面这个函数:

unsigned long get_zeroed_page(gfp_t gfp_mask);


释放页:

void __free_pages(struct page *page, unsigned int order);
void free_pages(unsigned long addr, unsigned int order);
void free_page(unsigned long addr);


释放时需要谨慎,仅释放属于你的页,释放错了地址或者用错了order值可能导致系统崩溃。

四、kmalloc

kmalloc()函数与用户空间的malloc函数类似,只不过多了一个flags参数。该函数是申请获得以字节为单位的一块内核内存。分配的内存在物理上是连续的。

void *kmalloc(size_t size, gfp_t flags)

void kfree(const void *ptr)


gfp_mask标志:

这些标志分为三类:行为修饰符、区修饰符、类型标志

行为标志描述
__GFP_WAIT分配器可以睡眠
__GFP_HIGH分配器可以访问紧急事件缓冲池
__GFP_IO分配器可以启动磁盘I/O
__GFP_FS分配器可以启动文件系统I/O
__GFP_COLD分配器应该使用高速缓存中快要淘汰出去的页
__GFP_NOWARN分配器将不打印失败警告
__GFP_REPEAT分配器在分配失败时重复进行分配,但是这次分配还存在失败的可能
__GFP_NOFALL分配器将无限的重复进行分配。分配不能失败
__GFP_NORETRY分配器在分配失败时不会重新分配
__GFP_NO_GROW由slab层内部使用
__GFP_COMP添加混合页元数据,在 hugetlb 的代码内部使用
区标志描述
__GFP_DMA从 ZONE_DMA 分配
__GFP_DMA32只在 ZONE_DMA32 分配 (注1)
__GFP_HIGHMEM从 ZONE_HIGHMEM 或者 ZONE_NORMAL 分配
类型标志描述
GFP_ATOMIC这个标志用在中断处理程序,下半部,持有自旋锁以及其他不能睡眠的地方
GFP_NOWAIT与 GFP_ATOMIC 类似,不同之处在于,调用不会退给紧急内存池。这就增加了内存分配失败的可能性
GFP_NOIO这种分配可以阻塞,但不会启动磁盘I/O。这个标志在不能引发更多磁盘I/O时能阻塞I/O代码,可能会导致递归
GFP_NOFS这种分配在必要时可能阻塞,也可能启动磁盘I/O,但不会启动文件系统操作。这个标志在你不能再启动另一个文件系统的操作时,用在文件系统部分的代码中
GFP_KERNEL这是常规的分配方式,可能会阻塞。这个标志在睡眠安全时用在进程上下文代码中。为了获得调用者所需的内存,内核会尽力而为。这个标志应当为首选标志
GFP_USER这是常规的分配方式,可能会阻塞。用于为用户空间进程分配内存时
GFP_HIGHUSER从 ZONE_HIGHMEM 进行分配,可能会阻塞。用于为用户空间进程分配内存
GFP_DMA从 ZONE_DMA 进行分配。需要获取能供DMA使用的内存的设备驱动程序使用这个标志。通常与以上的某个标志组合在一起使用。

五、vmalloc()

vmalloc函数的工作方式与kmalloc类似,只不过vmalloc分配的虚拟地址是连续的,但物理地址无需连续。

通过分配非连续的物理内存块,再修正页表,把内存映射到连续的虚拟地址空间中。这就带来一些消耗,带来性能上的影响,仅仅在不得已时才会使用。

void *vmalloc(unsigned long size)

void vfree(const void *addr)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息