免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1990 | 回复: 6
打印 上一主题 下一主题

[内存管理] BiscuitOS 技术分享[请勿回复] [复制链接]

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-03-30 10:17 |只看该作者 |正序浏览
本帖最后由 Buddy_Zhang1 于 2016-04-02 11:41 编辑

本帖用于 BiscuitOS 的技术分享.
BiscuitOS 介绍:   bbs.chinaunix.net/thread-4241956-1-1.html
本贴请勿回复,回复请到 bbs.chinaunix.net/thread-4241956-1-1.html
技术目录:
              1. BiscuitOS 上轻松使用红黑树
              2. BiscuitOS 上轻松使用哈希散列式
              3. BiscuitOS 上构建 GFP_ZONE_TABLE
              4. BiscuitOS - SLUB 分配器 slab page 大小计算方法
              5. BiscuitOS  中最大文件数计算方法:

论坛徽章:
11
程序设计版块每日发帖之星
日期:2015-09-09 06:20:00CU十四周年纪念徽章
日期:2016-05-16 11:11:112016科比退役纪念章
日期:2016-05-04 17:16:57程序设计版块每日发帖之星
日期:2016-02-20 06:20:00程序设计版块每周发帖之星
日期:2015-11-06 19:30:58程序设计版块每日发帖之星
日期:2015-09-12 06:20:00程序设计版块每日发帖之星
日期:2015-09-11 06:20:00每日论坛发贴之星
日期:2015-09-10 06:20:00程序设计版块每日发帖之星
日期:2015-09-10 06:20:00每日论坛发贴之星
日期:2015-09-09 06:20:0015-16赛季CBA联赛之四川
日期:2016-12-15 15:52:10
7 [报告]
发表于 2016-04-14 16:15 |只看该作者
这个东西我想参与,但不知道如何入手

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
6 [报告]
发表于 2016-04-02 10:53 |只看该作者
本帖最后由 Buddy_Zhang1 于 2016-04-02 11:02 编辑

BiscuitOS / Linux 中最大文件数计算方法:

在文件系统中,涉及一个最大文件数,本帖重点讲述如何计算这个值.
VFS 在初始化过程中,从内核获得当前系统物理页的数量(默认一个物理页的大小为 4K).
一个 struct file 分配的 inode 和 dcache 的空间大概是 1K,内核规定 struct file 占用的内存不能超过总内存的 10%,
于是计算公式如下:
1. mempages 为系统物理页的数量,每个物理页默认为 4K.
2. PAGE_SIZE 为物理页的大小,那么:
          "PAGE_SIZE / 1024"
    上面表达式的含义为,因为一个 struct file 分配的 inode 和 dcache 空间大小为 1K,那么一个物理页就能存储 4 个 struct file.
3. 那么全部物理页可以存储的 struct file 为:
          mempages * (PAGE_SIZE / 1024)
4. 由于内核规定,struct file 的内存空间只能为总内存的 10 %,所以最后文件数为:
         (mempages * (PAGE_SIZE / 1024)) / 10
5. 系统默认最大的文件数为 NR_FILE,通过上面的计算,最后最大文件数结果为:
         files_stat.max_files = max_t(unsigned long, n, NR_FILE);

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
5 [报告]
发表于 2016-04-01 08:23 |只看该作者
本帖最后由 Buddy_Zhang1 于 2016-04-01 08:25 编辑

BiscuitOS - SLUB 分配器 slab page 大小计算方法

在 BiscuitOS / Linux 上使用 kmem_cache_create() 创建缓存时,需要根据分配的对象的大小从 Buddy 分配器中获得空闲页来组成 slab page.
例如:
      struct kmem_cache *file_cachep = kmem_cache_create("File",sizeof(struct file),0,0,NULL);
内核通过 sizeof(struct file) 计算的值将从 Buddy 中获得的空闲页,然后将空闲页分成大小为 sizeof(struct file) 的 object.
储存这些 object 的 page 称为 slab page.本帖用于讨论一个 slab page 的大小.
内核根据 object 的 size 来计算 slab page 的大小,
1. 如果 object 的 size 很小,那么一个 slab page 就由一个 page 组成
2. 如果 object 的 size 很大,那么一个 slab page 可能有两个或多个 page 组成.
Slab page 的大小计算如下:
1. 在 slub 中一个 slab page 中规定最小的 object 数量为 slub_min_objects,其值可以为零,
    当 slub_min_objects 为零的时候,内核根据 cpu 的数量来进行赋值,其计算公式如下:
         slub_min_objects = 4 * (fls(nr_cpu_ids) + 1)
    其中函数 fls() 的作用是求参数的最高位的位数,如
         fls(00001100B) = 4
         fls(00001000B) = 4
         fls(00010000B) = 5
    fls() 的作用类似于向下除的效果.
    nr_cpu_ids 为系统 cpu 的个数.
    于是我们可以得出以下结论:
    (1) 在单 CPU 的体系中 slub_min_objects 为 8
    (2) 在双 CPU 的体系中 slub_min_objects 为 12
    (3) 在四 CPU 的体系中 slub_min_objects 为 16
2. 一个 slab page 最大 objects 数量为 max_objects,其计算如下:
    max_objects = (PAGE_SIZE << slub_max_order) / size;
    max_objects = min(min_objects,max_objects);
    上述表达式中 slub_max_order 为 3,其含义为最大的 slab_page 为 2^3,也就是 一个最大的 slab_page 可由 8 page 构成.
    size 为 object 的 size.
3. 获得上面两个值之后,内核进行 slab order 的计算,其过程如下:
    (1) 对一个 page 构成的 slab page 进行检测,如果在这种情况下, objects 的数量大于 slab page 规定的最大 object 的数量
          那么内核直接从 Buddy 分配器中获得最大 object 时的 page. 代码如下:
         if ((PAGE_SIZE << min_order) / size > MAX_OBJS_PER_PAGE)
                return get_order(size * MAX_OBJS_PER_PAGE) - 1;
     (2) 内核计算 object 的 size 对应的最小 order,其计算如下:
           order =  fls(min_objects * size - 1) - PAGE_SHIFT;
           通过上面对 min_objects 的计算,可以获得 object 的最小 order 如下表:



通过上面表的计算,我们可以看出不同的 object size 的 slab page 的最小 order 不同,由于 order 最小为 0,所以最最小 order 进行处理
          order = max(min_order,fls(min_objects * size - 1) - PAGE_SHIFT);
     (3) 获得最小 order 之后,就从最小 order 开始遍历,只要满足一个 slab page 中,浪费部分小于 1/16 就可以进行分配.因为 object size 大小不同,
         而 page 则按 PAGE_SIZE 进行对齐,所以无法避免一定的浪费,但内核规定只要浪费不超过 1/16 就可以进行分配.所以代码如下:
          for (order = max(min_order , fls(min_objects * size - 1) - PAGE_SHIFT) ; order <= max_order; order++) {
                   unsigned long slab_size = PAGE_SIZE << order;
         
                  if (slab_size < min_objects * size)
                         continue;
                  rem = slab_size % size;

                  if (rem <= slab_size / fract_leftover)
                          break;
}
     (4) 通过上面的计算之后获得最小 slab page 的 order,接着就可以从 Buddy 中获得对应的 page 来构成 slab page.

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
4 [报告]
发表于 2016-03-31 08:32 |只看该作者
BiscuitOS 上构建 GFP_ZONE_TABLE

本贴主要讲述如何构建 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_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) \
        )

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
3 [报告]
发表于 2016-03-30 19:36 |只看该作者
BiscuitOS 上轻松使用哈希散列式

哈希散列式作为 Linux 内核重要的数据结构,负责 Linux 的基本运作,由于其高效的数据管理能力和实用性,
内核也将哈希散列式作为其基本数据结构.本楼重点介绍如何在 BiscuitOS 和 Linux 使用哈希散列式.

哈希散列式的实现方法多种多样,这里以拉链法做为介绍,至于理论部分可以自行查找.
哈希散列式在内核中比较典型的运用如 inode 和 dentry 的管理,pid 管理以及本楼例子 kmap() 分配的内存管理.

内核中 KMAP 的区域就是使用哈希散列式进行管理.
从 ZONE_HIGHMEM 中的 Buddy System 中获得一个空闲 page 之后,使用 hash_ptr(page,PA_HASH_ORDER) 获得哈希散列式的 key.
内核维护了一个哈希散列表头 page_address_htable[1 << PA_HASH_ORDER]
内核通过 page_slot(page) 可以获得 hash 表头,在通过内部的 list_head 进行遍历.

哈希散列式使用例子:
源码位于 /BiscuitOS/tools/TestCase_Hash.c
  1. #include "linux/kernel.h"
  2. #include "linux/debug.h"
  3. #include "linux/types.h"
  4. #include "linux/slab.h"

  5. #define HEAD_NUM    3
  6. #define VALUE_NUM   100

  7. struct node {
  8.         struct hlist_node node;
  9.         int num;
  10. };

  11. struct hlist_head *phash = NULL;
  12. /*
  13. * TestCase_Hash
  14. */
  15. void TestCase_Hash(void)
  16. {
  17.         int i,k;
  18.         struct hlist_node *hnode;
  19.         struct hlist_head *head;
  20.         struct node *node;
  21.         int *value;

  22.         /* Prepare test data */
  23.         value = (int *)kmalloc(sizeof(int) * VALUE_NUM,GFP_KERNEL);
  24.         for(i = 0 ; i < VALUE_NUM ; i++)
  25.                 value[i] = i;

  26.         /* Prepare hash head array */
  27.         phash = (struct hlist_head *)kmalloc(sizeof(struct hlist_head) *
  28.                                         (1 << HEAD_NUM) , GFP_KERNEL);
  29.         /* Initialize hash head */
  30.         for(i = 0 ; i < (1 << HEAD_NUM) ; i++)
  31.                 INIT_HLIST_HEAD(&phash[i]);

  32.         /* Create Test node */
  33.         for(i = 0 ; i < VALUE_NUM ; i++) {
  34.                 node = (struct node *)kmalloc(sizeof(struct node),GFP_KERNEL);
  35.                 if(!node) {
  36.                         mm_err("No memory\n");

  37.                         /* Never water memory */
  38.                         goto bad_memory;
  39.                 }

  40.                 /* Prepare test data */
  41.                 node->num = value[i];
  42.                 /* Initialize the hash node */
  43.                 INIT_HLIST_NODE(&node->node);
  44.                 /* Get the hash head for node */
  45.                 head = &phash[hash_32(node->num,HEAD_NUM)];
  46.                 /* Add node into hash list */
  47.                 hlist_add_head(&node->node,head);
  48.         }

  49.         /* Trave all hash list */
  50.         for(i = 0 ; i < (1 << HEAD_NUM) ; i++) {
  51.                 mm_debug("HEAD %d:",i);
  52.                 if(!hlist_empty(&phash[i]))
  53.                         hlist_for_each_entry(node,hnode,&phash[i],node)
  54.                                 mm_debug("%d->",node->num);
  55.                 mm_debug("NULL\n");
  56.         }

  57.         /* Search data in hash list */
  58.         k = value[34];
  59.         head = &phash[hash_32(k,HEAD_NUM)];
  60.         mm_debug("Search %d in head %d\n",k,hash_32(k,HEAD_NUM));
  61.         if(!hlist_empty(head))
  62.                 hlist_for_each_entry(node,hnode,head,node)
  63.                         if(k == node->num)
  64.                                 mm_debug("Find the data %d\n",k);

  65.         /* Delete all node */
  66.         for(i = 0 ; i < (1 << HEAD_NUM) ; i++)
  67.                 while(!hlist_empty(&phash[i])) {
  68.                         node = hlist_entry(phash[i].first,struct node,node);
  69.                         hlist_del(&node->node);
  70.                         kfree(node);
  71.                 }

  72.         /* Final check */
  73.         for(i = 0 ; i < (1 << HEAD_NUM) ; i++) {
  74.                 if(!hlist_empty(&phash[i])) {
  75.                         mm_debug("HEAD %d REMAIN:",i);
  76.                         hlist_for_each_entry(node,hnode,&phash[i],node)
  77.                                 mm_debug("%d->",node->num);
  78.                         mm_debug("NULL\n");
  79.                 }
  80.         }
  81.         /* Free all and complete test */
  82.         kfree(phash);
  83.         kfree(value);

  84.         return;
  85. bad_memory:
  86.         for(i = 0 ; i < (1 << HEAD_NUM) ; i++)
  87.                 while(!hlist_empty(&phash[i])) {
  88.                         node = hlist_entry(phash[i].first,struct node,node);
  89.                         hlist_del(phash[i].first);
  90.                         kfree(node);
  91.                 }
  92.         kfree(phash);
  93.         kfree(value);
  94. }
复制代码

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:00程序设计版块每日发帖之星
日期:2016-02-14 06:20:0015-16赛季CBA联赛之吉林
日期:2016-03-23 17:25:0015-16赛季CBA联赛之浙江
日期:2016-04-01 08:25:0615-16赛季CBA联赛之山西
日期:2016-04-01 10:09:1915-16赛季CBA联赛之广夏
日期:2016-06-03 15:58:212016科比退役纪念章
日期:2016-07-28 17:42:5215-16赛季CBA联赛之广东
日期:2017-02-20 23:32:43
2 [报告]
发表于 2016-03-30 10:21 |只看该作者
BiscuitOS 上轻松使用红黑树

红黑树作为 Linux 内核重要的数据结构,负责 Linux 的基本运作,其重要程度和难易程度让人望而却步。
本节通过一个简单例子介绍红黑树在 BiscuitOS 以及 Linux 内核中的使用方法。

红黑树在 Linux 内核很多地方都有使用,比如进程调度的“完全公平调度”,虚拟空间对进程地址管理以及本章重点例子 vmalloc 内存区的管理。
红黑树由于其复杂的构成,这里不过多讲理论,本章侧重讲述如何使用红黑树。

1. 红黑树 API
rb_link_node 将红黑树节点插入到红黑树。
rb_insert_color 红黑树翻转颜色
rb_erase 从红黑树中移除一个节点
rb_first 获得红黑树的第一个节点
rb_next 获得红黑树节点的下一个节点

2. 红黑树在内核中的使用案例
VMALLOC 分配的虚拟地址通过红黑树进行管理,我们可以通过红黑树的 root 和虚拟地址大小来查找 VMALLOC 区的内容。
代码如下:(源码位置 /BiscuitOS/tools/TestCase_vmalloc.c)
  1.     void TestCase_vmalloc(void)
  2.     {
  3.             unsigned int addr;
  4.             struct rb_node *node;

  5.             addr = vmalloc(PAGE_SIZE);

  6.             addr = vmalloc(PAGE_SIZE);

  7.             addr = vmalloc(PAGE_SIZE);
  8.            
  9.             addr = vmalloc(PAGE_SIZE);
  10.            

  11.             /* Trave all node */
  12.             for(node = rb_first(&vmap_area_root) ; node ; node = rb_next(node)) {
  13.                     struct vmap_area *va;
  14.                     struct vm_struct *area;

  15.                     va = rb_entry(node,struct vmap_area,rb_node);
  16.                     area = va->private;
  17.                     printk(KERN_INFO "VA %p\n",(void *)(unsigned long)va->va_start);
  18.               }
  19.            
  20.             vfree(addr);
  21.     }
复制代码
3. 红黑树的使用案例。
(源码位置 /BiscuitOS/tools/TestCase_RB_tree.c)
  1. #include "linux/kernel.h"
  2. #include "linux/rbtree.h"
  3. #include "linux/debug.h"
  4. #include "linux/mm.h"
  5. #include "linux/gfp.h"
  6. #include "linux/slab.h"

  7. struct node {
  8.         struct rb_node node;
  9.         int num;
  10. };


  11. /*
  12. * Insert a node into RBtree.
  13. */
  14. int insert(struct node *data,struct rb_root *root)
  15. {
  16.         struct rb_node **link = &(root->rb_node);
  17.         struct rb_node *parent = NULL;

  18.         while(*link) {
  19.                 struct node *node = container_of(*link,struct node,node);

  20.                 parent = *link;
  21.                 if(data->num < node->num)
  22.                         link = &((*link)->rb_left);
  23.                 else if(data->num > node->num)
  24.                         link = &((*link)->rb_right);
  25.                 else
  26.                         return -1;
  27.         }
  28.         rb_link_node(&data->node,parent,link);
  29.         rb_insert_color(&data->node,root);
  30.         return 0;
  31. }

  32. /*
  33. * Search a node from RBtree.
  34. */
  35. struct node *search(int num,struct rb_root *root)
  36. {
  37.         struct rb_node *node = root->rb_node;

  38.         while(node) {
  39.                 struct node *data = container_of(node,struct node,node);

  40.                 if(num < data->num)
  41.                         node = node->rb_left;
  42.                 else if(num > data->num)
  43.                         node = node->rb_right;
  44.                 else
  45.                         return data;
  46.         }
  47.         return NULL;
  48. }

  49. /*
  50. * Delete a node from RBtree.
  51. */
  52. void delete(int num,struct rb_root *root)
  53. {
  54.         struct node *node = search(num,root);

  55.         if(node) {
  56.                 rb_erase(&node->node,root);
  57.                 kfree(node);
  58.         } else
  59.                 mm_err("%2d doesn't exits\n",num);
  60. }

  61. /*
  62. * Print all node
  63. */
  64. void print_all(struct rb_root *root)
  65. {
  66.         struct rb_node *node;

  67.         for(node = rb_first(root) ; node ; node = rb_next(node))
  68.                 mm_debug("%2d  ",rb_entry(node,struct node,node)->num);

  69.         mm_debug("\n");
  70. }

  71. /*
  72. * TestCase_RB_user
  73. */
  74. void TestCase_RB_user(void)
  75. {
  76.         struct rb_root root = RB_ROOT;
  77.         struct node *node;
  78.         int num,i,ret;
  79.         int value[30] = { 2  , 84 , 43 , 11 , 7  , 54 , 21 , 1  , 2  , 10 ,
  80.                           34 , 5  , 6  , 45 , 76 , 0  , 12 , 25 , 44 , 11 ,
  81.                           99 , 65 , 38 , 91 , 35 , 16 ,93  , 74 , 33 , 67 };

  82.         num = 30;

  83.         for(i = 0 ; i < num ; i++) {
  84.                 node = (struct node *)kmalloc(sizeof(struct node),GFP_KERNEL);
  85.                 if(!node) {
  86.                         mm_err("No Memory\n");

  87.                         /* Never Waste memory */
  88.                         for(i-- ; i >= 0 ; i--)
  89.                                 delete(value[i],&root);

  90.                         return;
  91.                 }

  92.                 node->num = value[i];

  93.                 /* Insert node into RBtree */
  94.                 ret = insert(node,&root);
  95.                 if(ret < 0) {
  96.                         mm_err("%2d has existed\n",node->num);
  97.                         kfree(node);
  98.                 }
  99.         }

  100.         mm_debug("First Check\n");
  101.         print_all(&root);

  102.         /* Delete a node */
  103.         delete(value[4],&root);
  104.         mm_debug("Second Check [%d]\n",value[4]);
  105.         print_all(&root);

  106.         /* Search a node */
  107.         node = search(value[2],&root);
  108.         mm_debug("Find %d\n",node->num);

  109.     /* Get prev node */
  110.         mm_debug("Prev num is %d\n",
  111.                         rb_entry(rb_prev(&node->node),struct node,node)->num);
  112.         /* Get next node */
  113.         mm_debug("Next num is %d\n",
  114.                         rb_entry(rb_next(&node->node),struct node,node)->num);
  115.         /* The first node */
  116.         mm_debug("Min num is %d\n",
  117.                         rb_entry(rb_first(&root),struct node,node)->num);
  118.         /* The last node */
  119.         mm_debug("Max num is %d\n",
  120.                         rb_entry(rb_last(&root),struct node,node)->num);

  121.         /* Free All node */
  122.         for(i = 0 ; i < num ; i++)
  123.                 delete(value[i],&root);

  124.         mm_debug("Last Check\n");
  125.         print_all(&root);
  126. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP