Chinaunix首页 | 论坛 | 博客
  • 博客访问: 36612
  • 博文数量: 4
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 43
  • 用 户 组: 普通用户
  • 注册时间: 2015-12-20 22:18
文章分类
文章存档

2016年(4)

我的朋友

分类: LINUX

2016-03-30 20:38:38

本贴主要讲述如何构建 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) \
        )
阅读(3084) | 评论(0) | 转发(0) |
1

上一篇:没有了

下一篇:BiscuitOS/Linux slab_page 大小计算方法

给主人留下些什么吧!~~