• 宋宝华:Linux内核中用GFP_ATOMIC申请内存究竟意味着什么?


    本文目的

    本文补充校正一些Linux内核开发者关于GFP_ATOMIC的认知不完整的地方,阐述GFP_ATOMIC与free内存watermark的关系,并明确什么时候应该用GFP_ATOMIC申请内存。目录:

    1. GFP_ATOMIC vs. GFP_KERNEL

    2. 内存水位,PF_MEMALLOC和GFP_ATOMIC

    3. 何时使用GFP_ATOMIC(一个patch分析)

    GFP_ATOMIC vs. GFP_KERNEL

    我们都知道,在中断、软中断、spinlock等原子上下文里面,申请内存,应该使用GFP_ATOMIC标记,譬如内核中有大量的kmalloc/GFP_ATOMIC的例子:

    本文补充校正一些Linux内核开发者关于GFP_ATOMIC的认知不完整的地方,阐述GFP_ATOMIC与free内存watermark的关系,并明确什么时候应该用GFP_ATOMIC申请内存。目录:

    1. GFP_ATOMIC vs. GFP_KERNEL

    2. 内存水位,PF_MEMALLOC和GFP_ATOMIC

    3. 何时使用GFP_ATOMIC(一个patch分析)

    GFP_ATOMIC vs. GFP_KERNEL

    我们都知道,在中断、软中断、spinlock等原子上下文里面,申请内存,应该使用GFP_ATOMIC标记,譬如内核中有大量的kmalloc/GFP_ATOMIC的例子:

    image

    对于不可睡眠的上下文,如果我们用常规的GFP_KERNEL这样的标记去申请内存,可能引发直接的内存reclaim,从而引起睡眠,所以GFP_KERNEL这种标记只适合进程上下文调用:

    image

    #define GFP_KERNEL     \
     (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
     
     #define __GFP_RECLAIM \
     ((__force gfp_t)(___GFP_DIRECT_RECLAIM|___GFP_KSWAPD_RECLAIM)
    
    

    内存水位,PF_MEMALLOC和GFP_ATOMIC

    image

    • HIGH: 系统的free内存大于HIGH水位的时候,是一个相对保险的值,不需要急着做内存回收(reclaim);

    • LOW: 系统的free内存达到LOW水位的时候,启动后台kswapd进行内存回收,回收的目标是让空闲内存达到HIGH水位;

    • MIN:系统应该保有的最小free内存,当空闲内存达到这个值的时候,kswapd的后台回收可能来不及了,一般用户在申请内存的时候,进行DIRECT RECLAIM。

    min水位一般是系统自动换算的,其具体值可以从/proc看出:

    # cat /proc/sys/vm/min_free_kbytes 
    45056
    

    而LOW水位一般是min125%,HIGH 一般是min150%。

    MIN水位以下的内存,只能被紧急情况下的用户申请到,最著名的紧急用户莫过于PF_MEMALLOC用户,task_struct设置了这个标记表示忽略MIN水位。比如回收内存的代码本身也可能需要申请内存,这个时候我们应该给它无限制的申请能力。典型地,比如kswapd就设置了这个标记,这个代码里面的注释也非常精彩:

    image

    如果我们不允许回收内存的代码申请min以下的内存,则回收内存的代码可以触发回收内存,这样“子子孙孙,无穷匮也”。

    当然,PF_MEMALLOC不是唯一的紧急用户,GFP_ATOMIC实际也是一个“半紧急”任务:

    • 说它“紧急”,是因为如果原子上下文申请内存失败,往往意味着相应的中断、软中断、spinlock内部的代码就会执行失败,而我们又不会因为这种失败,而去尝试内存回收,这显然比较惨,我们应该尽可能让GFP_ATOMIC申请成功;

    • 说它“半”,是因为它不至于紧急到PF_MEMALLOC这个程度,如果我们给它无限地申请到free内存为0的权力,则会导致PF_MEMALLOC没有内存了。想想,如果征粮队的人都饿死了,还怎么去征粮呢?

    所以,内存的设计选择是,当有人用GFP_ATOMIC申请内存的时候,允许它从MIN水位以下,申请一定数量的内存。什么叫“一定数量”呢?就是不能让GFP_ATOMIC导致free 内存触底,GFP_ATOMIC还包含了高优先级的含义:

    #define GFP_ATOMIC    \  
    (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
    

    注意这个里面的__GFP_HIGH不是HIGHMEM高端内存的意思,而是高优先级。

    当我们用GFP_ATOMIC申请内存的时候,内核的水位检查代码,会允许我们触及到MIN水位以下的1/2:

    image

    那么,“魔鬼”就是在画红圈的2行代码。但是,如果我们进一步深究,会发现,GFP_ATOMIC不只是触及1/2min,它甚至可以触及1/4min,因为GFP_ATOMIC中的__GFP_HIGH让ALLOC_HIGH成立,而__GFP_ATOMIC让ALLOC_HARDER成立:

    image

    所以,“魔鬼”又隐藏在了gfp_to_alloc_flags()的细节里。

    一个patch的例子

    在具体的工程实战中,我们建议:

    • 原子上下文使用GFP_ATOMIC

    比如在网络设备驱动drivers/net/ethernet中,就有大量的案例

    image

    在内存紧急的路径上(比如不想睡眠,要求低延迟;或者要求内存吃紧的情况下,仍然可以从min水位以下申请内存),哪怕是进程上下文,我们也建议可以考虑使用GFP_ATOMIC

    比如田涛童鞋最近在mm/zswap.c发的RFC patch:

    https://lore.kernel.org/linux-mm/1608894171-54174-2-git-send-email-tiantao6@hisilicon.com/

    image

    上面2个地方,其实都是可以睡眠的进程上下文,但是我们认为在frontendswap的路径上,我们对延迟敏感,对swap内存过程中进一步引发内存回收也担忧,因此,这里哪怕是非原子上下文,我们也没有使用GFP_KERNEL。

  • 相关阅读:
    [DeeplearningAI笔记]序列模型2.6Word2Vec/Skip-grams/hierarchical softmax classifier 分级softmax 分类器
    [DeeplearningAI笔记]序列模型2.3-2.5余弦相似度/嵌入矩阵/学习词嵌入
    [DeeplearningAI笔记]序列模型2.1-2.2词嵌入word embedding
    [DeeplearningAI笔记]序列模型1.10-1.12LSTM/BRNN/DeepRNN
    [DeeplearningAI笔记]序列模型1.7-1.9RNN对新序列采样/GRU门控循环神经网络
    [DeeplearningAI笔记]序列模型1.5-1.6不同类型的循环神经网络/语言模型与序列生成
    [DeeplearningAI笔记]序列模型1.3-1.4循环神经网络原理与反向传播公式
    [DeeplearningAI笔记]序列模型1.1-1.2序列模型及其数学符号定义
    The request lifetime scope cannot be created because the HttpContext is not available
    【转载】C#基础系列——小话泛型
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/16887700.html
Copyright © 2020-2023  润新知