Linux内核管理之分配掩码(三)

Linux内核管理之分配掩码(三)

分配掩码是linux内存管理中非常重要的一个参数,它影响着页面分配的整个流程。

分配掩码gfp_mask定义在include/linux/gfp.h文件中,这些标志位在Linux4.4内核中被重新归类,大致可以分成以下几类:

  • 内存管理区修饰符(zone modifier)
  • 移动修饰符(mobility and placement modifier)
  • 水位修饰符(watermark modifier)
  • 页面回收修饰符(page reclaim modifier)
  • 行动修饰符(action modifier)

内存管理区修饰符

内存管理区修饰符主要是用来表示应当从哪些内存管理区中来分配物理内存。内存管理区修饰符使用gfp_mask的最低4个比特位来表示

标志 描述
__GFP_DMA 从ZONE_DAM中分配内存
__GFP_DMA32 从ZONE_DMA32中分配内存
__GFP_HIGHMEM 优先从ZONE_HIGHMEN中分配内存

移动修饰符

移动修饰符主要用来指示分配出来的页面具有的移动属性。

在Linux2.6.24内核中,为了解决外碎片化的问题,引入了迁移类型,因此在分配内存时也需要指定所分配的页面具有哪些移动属性。

标志 描述
__GFP_MOVABLE 页面可以被迁移或者回收,比如内存规整
__GFP_RECLAIMABLE 在slab分配器中指定了SLAB_RECLAIM_ACCOUNT标志位,表示slab中使用的页面可以通过shrinkers来回收
__GFP_HARDWALL 使能cpuset内存分配策略
__GFP_THISNODE 从指定的内存节点中分配内存,并且没有回退机制
__GFP_ACCOUNT 分配过程中会被kmemcg记录

水位修饰符

水位修饰符用来控制是否可以访问系统紧急预留的内存

标志 描述
__GFP_HIGH 表示分配内存具有高优先级,并且这个分配请求是很有必要的,分配器可以使用紧急的内存池
__GFP_ATOMIC 表示分配内存的过程不能执行页面回收或者睡眠动作,并且具有很高的优先级。常用的场景是在中断上下文分配内存
__GFP_MEMALLOC 分配过程中允许访问所有的内存,包括系统预留的紧急内存
__GFP_NOMEMALLOC 分配过程中不允许访问系统预留的紧急内存

页面回收修饰符

标志 描述
__GFP_IO 允许开启I/O传输
__GFP_FS 允许调用底层的文件系统。。清楚这个标志位通常是为了避免死锁的发生
__GFP_DIRECT_RECLAIM 分配内存的过程中调用直接页面回收机制
__GFP_KSWAPD_RECLAIM 表示当到达内存管理区的低水位时会唤醒kswapd内核现成去异步地回收内存,直到内存管理区恢复到高水位为止
__GFP_RECLAIM 用来允许或禁止直接页面回收和kswapd内核线程
__GFP_REPEAT 当分配失败时会继续尝试
__GFP_NOFAIL 当分配失败时会无限地尝试下去,直到分配成功为止。当分配者希望分配内存不失败时,应该使用这个标志位,而不是自己写一个while循环来不断调用页面分配接口函数
__GFP_NORETRY 当直接页面回收和内存规整等机制都使用了还是无法分配内存时,就不用去重复尝试分配了,直接返回NULL

行动修饰符

标志 描述
__GFP_COLD 分配的内存不会马上被使用
__GFP_NOWARN 关闭分配过程中的一些错误报告
__GFP_ZERO 返回一个全部填充为0的页面
__GFP_NOTRACK 不被kmemcheck机制跟踪
__GFP_OTHER_NODE 在远端一个内存节点上分配

类型标志

对于内核开发者或者驱动开发者来说,要正确使用这些标志位是一件很困难的事情,因此定义了一些常用的分配掩码的组合,成为类型标志。类型标志提供了内核开发中常用的分配掩码的组合,推荐开发者使用这些类型标志。

一般来说,__GFP开头的就为分配掩码,GFP开头的(没有下划线)的就是类型标志

标志 描述
GFP_ATOMIC 调用者不能睡眠并且保证分配会成功。它可以访问系统预留的内存,这个标志位通常使用在中断处理程序、下半部、持有自旋锁或者其他不能睡眠的地方
GFP_KERNEL 内核分配内存最常用的标志位之一。它可能会被阻塞,即分配过程可能会睡眠
GFP_NOWAIT 分配不允许睡眠等待
GFP_NOIO 不需要启动任何的I/O操作。比如使用直接回收机制去丢弃干净的页面或者为slab分配的页面
GFP_NOFS 不会访问任何的文件系统的接口和操作
GFP_USER 通过用户空间的进程用来分配内存,这些内存可以被内核或者硬件使用。常用的一个场景是,硬件使用的DMA缓冲器要映射到用户空间,比如显卡的缓冲器
GFP_DMA/GFP_DMA32 使用ZOME_DMA或者ZOME_DMA32来分配内存
GFP_HIGHUSER 用户空间进程用来分配内存,优先使用ZONE_HIGHME,这些内存可以被映射到用户空间,内核空间不会直接访问这些内存,另外这些内存不能被迁移
GFP_HIGHUSER_MOVEABLE 类似GFP_HIGHUSER,但是页面可以被迁移

上面表格中的类型标志都是非常常用的分配掩码组合,在实际使用的过程汇总需要注意以下事项。

  • GFP_KERNEL
    GFP_KERNEL是最常见的内存分配掩码之一,主要用于分配内核使用的的内存,需要注意的是分配过程中会引起睡眠,这在中断上下文以及不能睡眠的内核路径里调用该分配掩码需要特别警惕,因为会引起死锁或者其他系统异常

  • GFP_ATOMIC
    GFP_ATOMIC这个标志位正好和GFP_KERNEL相反,它可以使用在不能睡眠的内存分配路径上,比如中断处理程序、软中断以及tasklet等。GFP_KERNEL可以让调用者睡眠等待系统页面回收来释放一些内存,但是GFP_ATOMIC不可以,所以有可能会分配失败。

  • GFP_USERGFP_HIGHUSERGFP_HIGHUSER_MOVEABLE
    这三个标志位都是为用户空间进程分配内存的。不同之处在于,GFP_HIGHUSER首先使用高端内存,GFP_HIGHUSER_MOVEABLE首先使用高端内存并且分配的内存具有可迁移性

  • GFP_NOINGFP_NOFS
    这两个标志位都会产生阻塞,它们用来避免某些其他的操作。
    GFP_NOIO表示分配过程中绝不会启动任何磁盘I/O的操作。
    GFP_NOFS表示分配过程中绝不会启动文件系统的相关操作。
    举个例子,假设进程A在执行打开文件的操作中需要分配内存,这时内存短缺了,那么进程A会睡眠等待,系统的OOM Killer机制会选择一个进程杀掉。假设选择了进程B,而进程B退出时需要执行一些文件系统的操作,这些操作可能会去申请锁,而恰巧进程A持有这个锁,所以死锁就发生了。

常用类型标志的使用

待完成

你可能感兴趣的:(Linux内存管理,linux,内存管理)