/ NOTES

如何更优雅的表示红黑树结点

如何更优雅的表示红黑树结点

我们正常红黑树的结点定义是:

struct rb_node
{
   struct rb_node *rb_parent;
   int rb_color;
   struct rb_node *rb_right;
   struct rb_node *rb_left;
};

但在 Linux 内核中,它是这么定义的:

struct rb_node {
unsigned long  __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
   /* The alignment might seem pointless, but allegedly CRIS needs it */
​
struct rb_root {
struct rb_node *rb_node;
};
​
#define rb_parent(r)   ((struct rb_node *)((r)->__rb_parent_color & ~3))

它把 parent 和 color 放在一起了,但为什么可以呢,明明 sizeof(struct rb_node *) == sizeof(unsigned long) ,并没有多出来的位置。

看下面的那行宏 #define rbparent(r) ((struct rbnode *)((r)->_rbparent_color & ~3)) ,它是把后两位置零之后,当作父结点的指针。那也就是说,它能保证父结点的指针后面两位都是 0 。

再看结构体后面的 attribute((aligned(sizeof(long)))) ,它告诉编译器确保 struct rb_node 的大小始终是 sizeof(long) 的倍数。这一点貌似没什么用, sizeof(struct rb_node *) == sizeof(long) ,这三个本来就一样大。

再看下面的注释 / The alignment might seem pointless, but allegedly CRIS needs it / —— ”对齐可能看起来毫无意义,但据称 CRIS 需要它“。

提交记录里说到:

[RBTREE] Add explicit alignment to sizeof(long) for struct rb_node.

Seems like a strange requirement, but allegedly it was necessary for struct address_space on CRIS, because it otherwise ended up being only byte-aligned. It’s harmless enough, and easier to just do it than to prove it isn’t necessary… although I really ought to dig out my etrax board and test it some time.

这个 CRIS 是 RISC ISA 的一系列 CPU,它可能对地址有某种要求。

像《深入理解计算机系统》第 3.9.3 节(P189)数据对齐中也有提到:

许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种类型的对象的地址必须是某个值 K(通常是 2、4 或 8)的倍数。这种对齐限制简化了形成处理器和内存系统之间接口的硬件设计。例如,假设一个处理器总是从内存中取 8 个字节,则地址必须为 8 的倍数。如果我们能保证将所有的 double 类型数据的地址对齐成 8 的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个 8 字节内存块中。

而这个 aligned(sizeof(long)) 对齐实际上是 kmalloc 对齐分配的函数。

kmalloc 在 32 位 CPU 上保证 4 字节对齐,在 64 位 CPU 上保证 8 字节对齐。也就是至少保证了 4 字节对齐。

所以它分配的地址都会是 4 的倍数,在二进制上后两位都会是 0。于是开发人员就将这两个位用于颜色。