zoukankan      html  css  js  c++  java
  • BiscuitOS/Linux 上构建 GFP_ZONE_TABLE【转】

    转自:http://blog.chinaunix.net/uid-30647659-id-5690543.html

    本贴主要讲述如何构建 BiscuitOS 或 Linux 上的 GFP_ZONE_TABLE 表.
    当在内核中分配内存的时候,必须指定分配的标志,如:
    kmalloc(size,gfp)
    kmem_cache_alloc(cache,gfp)
    内核通过 gfp 标志来判断从哪个区间分配内存,内核将物理内存按区间分配,在 Linux 中存在 ZONE_DMA,ZONE_NORMAL,ZONE_DMA32 和 ZONE_HIGHMEM 几个 zone.
    每个 zone 管理各自内存,调用者只要使用 gfp 标志就可以从不同的 gfp 区间获得内存.
    内核使用 gfp_zone(gfp) 函数来将 gfp 标志转换为对应的 zone.其实现如下:

    点击(此处)折叠或打开

    1. static inline enum zone_type gfp_zone(gfp_t flags)
    2. {
    3.     enum zone_type z;
    4.     int bit = (int)(flags & GFP_ZONEMASK);
    5.     z = (GFP_ZONE_TABLE >> (bit * ZONES_SHIFT)) &
    6.             ((1 << ZONES_SHIFT) - 1);
    7.     if(__builtin_constant_p(bit))
    8.         BUILD_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
    9.     else {
    10. #ifdef CONFIG_DEBUG_VM
    11.         BUG_ON((GFP_ZONE_BAD >> bit) & 1);
    12. #endif
    13.     }
    14.     return z;
    15. }
    从上面代码实现过程中可以看出这个转换依赖 GFP_ZONE_TABLE 表.其定义如下:
    #define GFP_ZONE_TABLE (
            (ZONE_NORMAL << 0 * ZONES_SHIFT)  
            | (OPT_ZONE_DMA << ___GFP_DMA * ZONES_SHIFT)
            | (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * ZONES_SHIFT)
            | (OPT_ZONE_DMA32 << ___GFP_DMA32 * ZONES_SHIFT) 
            | (ZONE_NORMAL << ___GFP_MOVABLE * ZONES_SHIFT)  
            | (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * ZONES_SHIFT) 
            | (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * ZONES_SHIFT)
            | (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * ZONES_SHIFT)
            )
    以及另外一个表 GFP_ZONE_BAD.
    #define GFP_ZONE_BAD (  
              1 << (___GFP_DMA | ___GFP_HIGHMEM)                 
            | 1 << (___GFP_DMA | ___GFP_DMA32)              
            | 1 << (___GFP_DMA32 | ___GFP_HIGHMEM)                  
            | 1 << (___GFP_DMA | ___GFP_DMA32 | ___GFP_HIGHMEM)      
            | 1 << (___GFP_MOVABLE | ___GFP_HIGHMEM | ___GFP_DMA)  
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA)   
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_HIGHMEM)   
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA | ___GFP_HIGHMEM)
    )
    内核是如何构建这两张表?本贴重点讲述内核如何构建这两张表.
    首先内核将一个节点分作不同的管理区,每个管理区使用 struct zone 进行管理,内核在 zone 基本分为:
    ZONE_DMA,ZONE_NORMAL,ZONE_DMA32 和 ZONE_HIGHMEM,每个管理区使用 gfp 标志表示为:
    #define ___GFP_DMA               0x01u
    #define ___GFP_HIGHMEM     0x02u
    #define ___GFP_DMA32          0x04u
    #define ___GFP_MOVABLE     0x08u
    在 gfp 标志中低 3 位来表示内存从哪个 zone 获得,其掩码为:
    #define GFP_ZONEMASK  (__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32 | __GFP_MOVABLE)
    内核规定 ___GFP_DMA,___GFP_HIGHMEM 和 ___GFP_DMA32 其两个或全部不能同时存在于 gfp 标志中.
    四个标志排列组合后可以获得下表:
    序号 ___GFP_DMA ___GFP_HIGHMEM ___GFP_DMA32 ___GFP_MOVABLE 组合结果
    0 0 0 0 0 从 ZONE_NORMAL 中分配
    1 1 0 0 0 从 ZONE_NORMAL 或
    ZONE_DMA 中分配
    2 0 1 0 0 从 ZONE_NORMAL 或
    ZONE_HIGHMEM 中分配
    3 1 1 0 0 不能同时满足,错误
    4 0 0 1 0 从 ZONE_NORMAL 或
    ZONE_DMA32 中分配
    5 1 0 1 0 不能同时满足,错误
    6 0 1 1 0 不能同时满足,错误
    7 1 1 1 0 不能同时满足,错误
    8 0 0 0 1 从 ZONE_NORMAL 或
    ZONE_MOVABLE 获得
    9 1 0 0 1 从 ZONE_NORMAL 或
    (ZONE_DMA + ZONE_MOVALE) 获得
    a 0 1 0 1 从 ZONE_MOVABLE 获得
    b 1 1 0 1 不能同时满足,错误
    c 0 0 1 1 从 ZONE_DMA
    d 1 0 1 1 不能同时满足,错误
    e 0 1 1 1 不能同时满足,错误
    f 1 1 1 1 不能同时满足,错误
    从上面的表很容易构建 GFP_ZONE_BAD,将所有错误情况或起来就行.
    1. (___GFP_DMA      | ___GFP_HIGHMEM)
    2. (___GFP_DMA      | ___GFP_DMA32)
    3. (___GFP_DMA32 | ___GFP_HIGHMEM )
    4. (___GFP_DMA32 | ___GFP_HIGHMEM | ___GFP_DMA)
    5. (___GFP_DMA     | ___GFP_HIGHMEM | ___GFP_MOVABLE )
    6. (___GFP_DMA     | ___GFP_DMA32       | ___GFP_MOVABLE)
    7. (___GFP_DMA32 | ___GFP_HIGHMEM | ___GFP_MOVABLE)
    8. (___GFP_DMA32 | ___GFP_DMA32       | ___GFP_HIGHMEM | ___GFP_MOVABLE)
    将上面 8 种情况合成 BAD TABLE 如下:
    #define GFP_ZONE_BAD (  
              1 << (___GFP_DMA | ___GFP_HIGHMEM)                 
            | 1 << (___GFP_DMA | ___GFP_DMA32)              
            | 1 << (___GFP_DMA32 | ___GFP_HIGHMEM)                  
            | 1 << (___GFP_DMA | ___GFP_DMA32 | ___GFP_HIGHMEM)      
            | 1 << (___GFP_MOVABLE | ___GFP_HIGHMEM | ___GFP_DMA)  
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA)   
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_HIGHMEM)   
            | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA | ___GFP_HIGHMEM)
    )
    剩下的 8 种情况就是可以分配.分别为:
    1.  0
    2. (___GFP_DMA)
    3. (___GFP_HIGHMEM)
    4. (___GFP_DMA32)
    5. (___GFP_MOVABLE)
    6. (___GFP_DMA           | ___GFP_MOVABLE)
    7. (___GFP_HIGHMEM | ___GFP_MOVABLE)
    8. (___GFP_DMA32      | ___GFP_MOVABLE)
    构建初期表为
    )
    #define TABLE (                                                                 
            (1 << 0)                                                                       
          |  (1<<  ___GFP_DMA)                                                   
          |  (1 <<  ___GFP_HIGHMEM)                                         
          |  (1 <<  ___GFP_DMA32)                                               
          |  (1 <<  ___GFP_MOVABLE)                                           
          |  (1 << ___GFP_DMA           | ___GFP_MOVABLE)       
          |  (1 << ___GFP_HIGHMEM | ___GFP_MOVABLE)       
          |  (1 << ___GFP_DMA32      | ___GFP_MOVABLE)       
    )将对应的 zone 填充进去,其中
    OPT_ZONE_DMA 代表 ___GFP_NORMAL 或者 ___GFP_NORMAL
    OPT_ZONE_HGIHMEM 代表 ___GFP_NORMAL 或者 ___GFP_HIGHMEM
    OPT_ZONE_DMA32 代表 ___GFP_NORMAL 或者 ___GFP_DMA32
    根据表的分析,可获得下面结论:
    #define TABLE (                                                                 
            (ZONE_NORMAL << 0)                                                                       
          |  (OPT_ZONE_DMA <<  ___GFP_DMA)                                                   
          |  (OPT_ZONE_HIGHMEM <<  ___GFP_HIGHMEM)                                         
          |  (OPT_ZONE_DMA32 <<  ___GFP_DMA32)                                               
          |  (ZONE_NORMAL <<  ___GFP_MOVABLE)                                           
          |  (OPT_ZONE_DMA << ___GFP_DMA           | ___GFP_MOVABLE)       
          |  (ZONE_MOVABLE << ___GFP_HIGHMEM | ___GFP_MOVABLE)       
          |  (OPT_ZONE_DMA32 << ___GFP_DMA32      | ___GFP_MOVABLE)       
    )
    由于不同的平台会使用不同数量的 zone 管理区,常见的 zone 分配为 ZONE_NORMAL 和 ZONE_HIGHMEM 搭配.
    于是内核将 TABLE 的位宽使用 ZONES_SHIFT 表示,如一个具有 ZONE_DMA,ZONE_DMA32,ZONE_NORMAL 和 ZONE_HIGHMEM 的平台
    ZONES_SHIFT 为 2,表示 GFP_ZONE_TABLE 每个选项的位宽为 2.于是最终的表为:
    #define GFP_ZONE_TABLE (
            (ZONE_NORMAL << 0 * ZONES_SHIFT)  
            | (OPT_ZONE_DMA << ___GFP_DMA * ZONES_SHIFT)
            | (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * ZONES_SHIFT)
            | (OPT_ZONE_DMA32 << ___GFP_DMA32 * ZONES_SHIFT) 
            | (ZONE_NORMAL << ___GFP_MOVABLE * ZONES_SHIFT)  
            | (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * ZONES_SHIFT) 
            | (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * ZONES_SHIFT)
            | (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * ZONES_SHIFT)
            )
     
  • 相关阅读:
    mac sourceTree 每次操作提示需要密码
    docker-compose.yml开机启动
    详解Oracle 21c 中的只读Oracle主⽬录特性 (ROOH)
    使用kubeadm一键部署kubernetes集群
    Ubuntu18.04 开机自启动(需要 sudo 权限)
    nginx加载vue3 打包后的静态文件
    使用Docker搭建Nextcloud SSL站点
    Docker+Selenium+TestNG+Maven+Jenkins环境搭建
    Windows Server 2016安装AD并开启SSL
    Centos 环境配置内网 Yum 源
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13515028.html
Copyright © 2011-2022 走看看