linux 內核 紅黑樹接口說明

紅黑樹(rbtree)在linux內核中使用非常廣泛,cfs調度任務管理,vma管理等。本文不會涉及關于紅黑樹插入和刪除時的各種case的詳細描述,感興趣的讀者可以查閱其他資料。本文主要聚焦于linux內核中經典rbtree和augment-rbtree操作接口的說明。

1、基本概念

二叉樹:每個結點最多2棵子樹,無其它限制了。

二叉查找樹(二叉排序樹/二叉搜索樹):首先它是二叉樹,左子樹上所有結點的值小于它根結點的值,右子樹上所有結點的值大于它根結點的值(遞歸定義).

二叉平衡樹:也稱為平衡二叉樹,它是"平衡二叉搜索樹"的簡稱。首先它是"二叉搜索樹",其次它是平衡的,即它的每一個結點的左子樹的高度和右子樹的高度差至多為1。

紅黑樹性質:
紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色為紅色或黑色。除二叉查找樹強制一般要求以外,對于任何有效的紅黑樹增加了如下的額外要求:

性質1. 節點是紅色或黑色。

性質2. 根是黑色。

性質3. 所有葉子都是黑色(葉子是NULL節點)。

性質4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)

性質5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

?* red-black trees properties: ?http://en.wikipedia.org/wiki/Rbtree

?*

?* ?1) A node is either red or black

?* ?2) The root is black

?* ?3) All leaves (NULL) are black

?* ?4) Both children of every red node are black

?* ?5) Every simple path from root to leaves contains the same number of black nodes.

?*

?* ?4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two

?* ?consecutive red nodes in a path and every red node is therefore followed by

?* ?a black. So if B is the number of black nodes on every simple path (as per

?* ?5), then the longest possible path due to 4 is 2B.

?*

?* ?We shall indicate color with case, where black nodes are uppercase(大寫字母) and red nodes will be lowercase(小寫字母).

?* ?Unknown color nodes shall be drawn as red within parentheses and have some accompanying text comment.

linux內核中的紅黑樹分為兩類,一類是經典的紅黑樹,用于存放key/value鍵值對,另一類是增強型紅黑樹(VMA是內核中典型的augment-rbtree)。

增強型rbtree是一種在每個節點中存儲了“一些”額外數據的rbtree,其中節點N的額外數據必須是根為N的子樹中所有節點內容的函數。

這些數據可用于為rbtree增加一些新功能。增強rbtree是建立在基本rbtree基礎設施之上的可選功能。

需要此特性的rbtree用戶在插入和刪除節點時必須使用用戶提供的增強回調調用增強函數。

注意內核紅黑樹的實現將部分工作留給了用戶來實現:用戶需要編寫自己的樹搜索和插入函數調用所提供的rbtree函數,鎖也留給rbtree代碼的用戶。

2、數據結構

/*linux內核中,rbtree作為通用數據結構類似鏈表是嵌入到用戶數據結構內部,在用戶數據結構中存放自己的數據*/
struct rb_node {/*父節點,由于struct rb_node是long對齊,所以其地址低3-0bit或7-0bit未使用,低2位被用來作為顏色標志使用*/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_node為long字節對齊,其地址最少也是4字節對齊,所以其成員__rb_parent_color用于存放其parent的地址,同時低2bit可以存放自身的----顏色屬性。/*根節點*/
struct rb_root {struct rb_node *rb_node;
};/*節點顏色,默認插入節點為紅色*/
#define	RB_RED		0
#define	RB_BLACK		1/*父節點地址, &~3 去掉顏色標志位*/
#define rb_parent(r)   		((struct rb_node *)((r)->__rb_parent_color & ~3))#define RB_ROOT		(struct rb_root) { NULL, }
#define RB_ROOT_CACHED 	(struct rb_root_cached) { {NULL, }, NULL }#define __rb_parent(pc)    ((struct rb_node *)(pc & ~3))/*pc節點的顏色*/
#define __rb_color(pc)     ((pc) & 1)
#define __rb_is_black(pc)  __rb_color(pc)
#define __rb_is_red(pc)    (!__rb_color(pc))/*rb->__rb_parent_color的顏色*/
#define rb_color(rb)       __rb_color((rb)->__rb_parent_color)
#define rb_is_red(rb)      __rb_is_red((rb)->__rb_parent_color)
#define rb_is_black(rb)    __rb_is_black((rb)->__rb_parent_color)/*返回內嵌struct rb_node的數據結構*/
#define	rb_entry(ptr, type, member) container_of(ptr, type, member)#define RB_EMPTY_ROOT(root)  (READ_ONCE((root)->rb_node) == NULL)/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
#define RB_EMPTY_NODE(node)  \((node)->__rb_parent_color == (unsigned long)(node))/*注意,這里是賦值操作*/
#define RB_CLEAR_NODE(node)  \((node)->__rb_parent_color = (unsigned long)(node))

3、接口說明

3.1、rbtree插入紅黑樹節點

3.1.1、經典rbtree插入紅黑樹節點

在將數據插入rbtree之前,需要用戶實現查找函數,查找插入節點應該插入到rbtree root中的位置,建立鏈接后,才能將其插入到root中;

系統無法知道用戶數據存放規則,將節點存放到rbtree中的位置的查找工作交給用戶來處理。

通過rb_link_node(...)接口設置node要被插入到parent下面,建立位置鏈接關系
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,struct rb_node **rb_link)
{/*設置node__rb_parent_color的值,顏色屬性為紅色*/node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL;*rb_link = node;
}在樹中插入數據包括首先搜索插入新節點的位置,然后插入節點并重新平衡(“重新上色”)樹。
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{__rb_insert(node, root, dummy_rotate);
}
節點插入的工作交給__rb_insert來處理。下面是__rb_insert函數原型:
static __always_inline void __rb_insert(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))其中augment_rotate函數指針傳入旋轉回調函數,經典紅黑樹中未使用,傳入啞旋轉回調函數dummy_rotate;經典紅黑樹只是存儲節點之間的順序關系,無其他"額外"信息,所以其struct rb_augment_callbacks 增強回調函數全部實現為空;
/** Non-augmented rbtree manipulation functions.(非增強紅黑樹操作功能函數)** We use dummy augmented callbacks here, and have the compiler optimize them* out of the rb_insert_color() and rb_erase() function definitions.*/static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {}
static inline void dummy_copy(struct rb_node *old, struct rb_node *new) {}
static inline void dummy_rotate(struct rb_node *old, struct rb_node *new) {}static const struct rb_augment_callbacks dummy_callbacks = {dummy_propagate, dummy_copy, dummy_rotate
};/** Please note - only struct rb_augment_callbacks and the prototypes for* rb_insert_augmented() and rb_erase_augmented() are intended to be public.* The rest are implementation details you are not expected to depend on.** See Documentation/rbtree.txt for documentation and samples.*/struct rb_augment_callbacks {void (*propagate)(struct rb_node *node, struct rb_node *stop);void (*copy)(struct rb_node *old, struct rb_node *new);void (*rotate)(struct rb_node *old, struct rb_node *new);
};
對于augment-rbtree(增強紅黑樹)rb_augment_callbacks的定義可以通過下面的宏來實現;
/*這個宏定義的內容比較長,定義了augment回調函數接口以及對應的struct rb_augment_callbacks rbname 結構體*/
#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield,	\rbtype, rbaugmented, rbcompute)		\
static inline void							\
rbname ## _propagate(struct rb_node *rb, struct rb_node *stop)		\
{									\while (rb != stop) {						\rbstruct *node = rb_entry(rb, rbstruct, rbfield);	\rbtype augmented = rbcompute(node);			\if (node->rbaugmented == augmented)			\break;						\node->rbaugmented = augmented;				\rb = rb_parent(&node->rbfield);				\}								\
}									\
static inline void							\
rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new)		\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\
}									\
static void								\
rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new)	\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\old->rbaugmented = rbcompute(old);				\
}									\
rbstatic const struct rb_augment_callbacks rbname = {			\rbname ## _propagate, rbname ## _copy, rbname ## _rotate	\
};

3.1.2、augment-rbtree(增強紅黑樹)插入紅黑樹節點

在插入時,用戶必須更新通向插入節點的路徑上的增強信息,然后像往常一樣調用rb_link_node()和rb_augment_inserted(),而不是通常的rb_insert_color()調用。

如果rb_augment_inserts()重新平衡了rbtree,它將回調為用戶提供的函數,以更新受影響子樹上的增強信息。

/** Fixup the rbtree and update the augmented information when rebalancing.** On insertion, the user must update the augmented information on the path* leading to the inserted node, then call rb_link_node() as usual and* rb_augment_inserted() instead of the usual rb_insert_color() call.* If rb_augment_inserted() rebalances the rbtree, it will callback into* a user provided function to update the augmented information on the* affected subtrees.*/
static inline void rb_insert_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{__rb_insert_augmented(node, root, augment->rotate);
}/** Augmented rbtree manipulation functions.** This instantiates the same __always_inline functions as in the non-augmented* case, but this time with user-defined callbacks.*/void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
{__rb_insert(node, root, augment_rotate);
}

和經典紅黑樹插入節點操作一樣,最后的后都是留給__rb_insert來處理的。區別在于需要提供augmet->rotate的實現。

3.2、rbtree刪除紅黑樹節點

3.2.1、經典rbtree刪除紅黑樹節點

void rb_erase(struct rb_node *node, struct rb_root *root)
{struct rb_node *rebalance;rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);if (rebalance)____rb_erase_color(rebalance, root, dummy_rotate);
}/* Fast replacement of a single node without remove/rebalance/add/rebalance */
void rb_replace_node(struct rb_node *victim, struct rb_node *new,struct rb_root *root)
{struct rb_node *parent = rb_parent(victim);/* Set the surrounding nodes to point to the replacement */__rb_change_child(victim, new, parent, root);if (victim->rb_left)rb_set_parent(victim->rb_left, new);if (victim->rb_right)rb_set_parent(victim->rb_right, new);/* Copy the pointers/colour from the victim to the replacement */*new = *victim;
}

3.2.2、augment-rbtree(增強紅黑樹)刪除紅黑樹節點

當擦除節點時,用戶必須調用rb_erase_augmented()而不是rb_erase()。Rb_erase_augmented()回調用戶提供的函數來更新受影響子樹上的增強信息。

static __always_inline void rb_erase_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);if (rebalance)__rb_erase_color(rebalance, root, augment->rotate);
}	

3.3、rbtree節點遍歷

/*如果rbtree中的節點是按順存放的話,rb_first返回最小值節點*/

struct rb_node *rb_first(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_left)n = n->rb_left;return n;
}/*如果rbtree中的節點是按順存放的話,rb_last返回最大值節點*/
struct rb_node *rb_last(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_right)n = n->rb_right;return n;
}/*如果rbtree中的節點是按順存放的話,rb_next返回值比node節點值大的節點*/
struct rb_node *rb_next(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a right-hand child, go down and then left as far* as we can.*/if (node->rb_right) { /*node右子樹上的值都比node大*/node = node->rb_right;while (node->rb_left) /*一直尋找左子樹*/node=node->rb_left;return (struct rb_node *)node;}/** No right-hand children. Everything down and left is smaller than us,* so any 'next' node must be in the general direction of our parent.* Go up the tree; any time the ancestor is a right-hand child of its* parent, keep going up. First time it's a left-hand child of its* parent, said parent is our 'next' node.*//*node無右子樹且node的parent存在:1、如果node為parent的左節點,則返回parent(parent比node大);2、node為其parent的右節點(parent比node小),則繼續遞歸往上找(如果一直為右節點,表明node是以當前parent為root的這棵子樹上的最大值),直到找到node為parent的左節點時返回其parent(parent比左子樹所以節點都大);*/while ((parent = rb_parent(node)) && node == parent->rb_right)node = parent;return parent; /*這里返回的是parent*/
}/*如果rbtree中的節點是按順存放的話,rb_next返回值比node節點值小的節點*/
struct rb_node *rb_prev(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a left-hand child, go down and then right as far* as we can.*/if (node->rb_left) {  /*node左子樹上的值都比node小*/node = node->rb_left; while (node->rb_right) /*一直找右子樹*/node=node->rb_right;return (struct rb_node *)node;}/** No left-hand children. Go up till we find an ancestor which* is a right-hand child of its parent.*//*node無左子樹且node的parent存在:1、如果node為parent的右節點,則返回parent(parent比node小);	2、node為其parent的左節點(parent比node大),則繼續遞歸往上找,(如果一直為左節點,表明node是以當前parent為root的這棵子樹上的最小值),直到找到node為parent的右節點時返回其parent(parent比右子樹所以節點都小);*/while ((parent = rb_parent(node)) && node == parent->rb_left)node = parent;return parent; /*這里返回的是parent*/
}

上面四個宏可以用于遍歷紅黑樹中的節點:

for (node = rb_first(&mytree); node; node = rb_next(node)){

...
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/44514.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/44514.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/44514.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于主成分分析PCA的一維時間序列信號降噪方法(Python)

主成分分析PCA是面向模式分類的特征提取最典型的工具,是滿足上述映射準則的一種數據壓縮的方法。作為經典的特征提取方法,是在不減少原始數據所包含的內在信息前提下,將原始數據集轉化為由維數較少的“有效”特征成分來表示,使其在…

GD32F303之CAN通信

1、CAN時鐘 GD32F303主時鐘頻率最大是120Mhz,然后APB1時鐘最大是60Mhz,APB2時鐘最大是120Mhz,CAN掛載在APB1總線上面 所以一般CAN的時鐘頻率是60Mhz,這個頻率和后面配置波特率有關 2、GD32F303時鐘配置 首先我們知道芯片有幾個時鐘 HXTAL:高速外部時鐘&#xff1…

用理解與包容對待阿斯伯格綜合征患者

在我們的社會中,存在著這樣一個特殊的群體——阿斯伯格綜合征患者。他們在社交互動、溝通交流和行為模式上有著獨特的表現,需要我們以正確的方式去理解和對待。 我們要認識到阿斯伯格綜合征是一種神經發育障礙,而非個人的選擇或過錯。患者可能…

AI Earth——中國城市綠地對大氣污染干沉降作用估計數據集(DDEP)應用APP

基于數學模型量化植被的干沉降過程,突破傳統站點尺度研究的局限性,結合多源衛星遙感產品形成2000-2020年中國城市綠地對PM2.5和PM10的干沉降量估計柵格數據集,對城市大氣污染防治、綠地區域規劃和城市可持續發展有重要意義. 應用結果 代碼 #導入安裝包 import os import …

本地部署,強大的面部修復與增強網絡CodeFormer

目錄 什么是 CodeFormer? 技術原理 主要功能 應用場景 本地部署 運行結果 結語 Tip: 在圖像處理和計算機視覺領域,面部修復和增強一直是一個備受關注的研究方向。近年來,深度學習技術的飛速發展為這一領域帶來了諸多突破性…

c++:面向對象的繼承特性

什么是繼承 (1)繼承是C源生支持的一種語法特性,是C面向對象的一種表現 (2)繼承特性可以讓派生類“瞬間”擁有基類的所有(當然還得考慮權限)屬性和方法 (3)繼承特性本質上是為了代碼復用 (4)類在C編譯器的內部可以理解為結構體,派…

BJT交流分析+共發射極(CE)放大器+單片機的中斷系統(中斷的產生背景+使用中斷重寫秒表程序+中斷優先級)

2024-7-10,星期三,16:58,天氣:陰,心情:晴。今天終于陰天啦,有點風涼快一點了,不然真要受不了了,然后沒有什么特殊的事情發生,繼續學習啦,加油加油…

yolov5中訓練長條型目標召回率低問題

對于長條目標長寬比比較大的目標,如長1000pix,寬度10pix等在訓練時masic數據增強圖片中會出現有的圖片中標簽遺失問題,將原來標注好的目標,但是在增強圖片中沒有標簽,就會導致召回率低的問題。 在訓練代碼中augmentations.py文件…

MATLAB——運算符

文章目錄 MATLAB——運算符算數運算符矩陣的算數運算 關系運算邏輯運算符運算優先級 MATLAB——運算符 算數運算符 MATLAB中算數運算符包括加、減、乘、除、點乘、點除等。其運算規則如下表所示: 運算符運算規則ABA與B相加(A、B為數值或矩陣&#xff0…

一鍵安裝ros及出現問題的解決方案

wget http://fishros.com/install -O fishros && . fishroscatkin_make時出現報錯如下 catkin_make時出現錯誤提示如下: catkin_make Base path: /home/efsz/zmq_to_ros Source space: /home/efsz/zmq_to_ros/src Build space: /home/efsz/zmq_to_ros/build…

科普文:HTTPS協議

概敘 HTTPS(Secure Hypertext Transfer Protocol)即安全超文本傳輸協議,是一個安全通信通道。用于計算機網絡的安全通信,已經在互聯網得到廣泛應用。 HTTPS 是基于 HTTP 的擴展,其相當于 HTTP協議SSL(安全套…

0708,LINUX目錄相關操作

主要是冷氣太足感冒了&#xff0c;加上少吃藥抗藥性差&#xff0c;全天昏迷&#xff0c;學傻了學傻了 cat t_chdir.c #include <stdio.h> #include <unistd.h> #include <error.h> #include <errno.h> #include <sys/stat.h>int main(int argc…

魯棒控制器設計方法:systune,hinfsyn,musyn,slTuner

systune和hinfsyn更側重于基于數學模型的控制器設計&#xff0c;而musyn則特別考慮了系統的不確定性。slTuner則提供了在Simulink環境中進行控制器設計和調整的能力。 指定結構的控制器整定&#xff1a;systune, hinfstruct廣義控制對象整定&#xff1a;musyn, mixed musyn, h…

應急響應-ELK日志分析系統

&#x1f3bc;個人主頁&#xff1a;金灰 &#x1f60e;作者簡介:一名簡單的大一學生;易編橙終身成長社群的嘉賓.? 專注網絡空間安全服務,期待與您的交流分享~ 感謝您的點贊、關注、評論、收藏、是對我最大的認可和支持&#xff01;?? &#x1f34a;易編橙終身成長社群&#…

2024年PMP考試備考經驗分享

PMP是項目管理領域最重要的認證之一,本身是IT行業比較流行的證書&#xff0c;近幾年在臨床試驗領域也漸漸流行起來&#xff0c;是我周圍臨床項PM幾乎人手一個的證書。 考試時間&#xff1a;PMP認證考試形式為180道選擇題&#xff0c;考試時間為3小時50分。 考試計劃&#xff…

NFS綜合項目

現有主機 node01 和 node02&#xff0c;完成如下需求&#xff1a; 1、在 node01 主機上提供 DNS 和 WEB 服務 2、dns 服務提供本實驗所有主機名解析 3、web服務提供 www.rhce.com 虛擬主機 4、該虛擬主機的documentroot目錄在 /nfs/rhce 目錄 5、該目錄由 node02 主機提供的NFS…

Spring——自動裝配Bean

自動裝配是Spring滿足bean依賴的一種方式 Spring會在上下文中自動尋找&#xff0c;并自動給bean裝配屬性 在Spring中有三種裝配的方式&#xff1a; 1. 在xml中顯示配置 2. 在java中顯示配置 3. 隱式的自動裝配bean【重要】 測試 記得創建Cat、Dog、People類 public clas…

NI 5G大規模MIMO測試臺:將理論變為現實

目錄 概覽引言MIMO原型驗證系統MIMO原型驗證系統硬件LabVIEW通信系統設計套件&#xff08;簡稱LabVIEW Communications&#xff09;CPU開發代碼FPGA代碼開發硬件和軟件緊密集成 LabVIEW Communications MIMO應用框架MIMO應用框架特性單用戶MIMO和多用戶MIMO基站和移動站天線數量…

常用控件(三)

輸入類控件 QLineEditQTextEditQComboBoxQSpinBoxQDateTimeEditQDialQSlider QLineEdit QLineEdit用來表示單行輸入框&#xff0c;可以輸入一段文本&#xff0c;但是不能換行; 核心屬性: 屬性說明text輸入框中的文本inputMask輸入內容格式約束maxLength最大長度frame是否添加邊…

推薦算法有哪些?——協同過濾、內容推薦、DNN、FM、DeepFM

推薦算法是機器學習和數據挖掘領域的一個重要研究方向&#xff0c;旨在向用戶或群體推薦可能感興趣的物品或信息。 以下是對您提到的幾種推薦算法的詳細介紹&#xff1a; 1. 協同過濾&#xff08;Collaborative Filtering&#xff09; 定義&#xff1a;協同過濾是一種基于用…