主要移植了內核中的 list,rbtree。使得這2個數據結構在用戶態程序中也能使用。
同時用 cpputest 對移植后的代碼進行了測試。(測試代碼其實也是使用這2個數據結構的方法)
內核代碼的如下文件:(內核版本 v3.2 debian 7.5源碼)
- include/linux/list.h (刪除了 hlist 相關內容)
- include/linux/rbtree.h
- lib/rbtree.c
對上面的代碼進行了一些簡化,只留了常用的函數。同時刪除了其中和內核相關的部分。
主要內容:
- list 介紹 (循環雙向鏈表)
- rbtree 介紹
1. list 介紹 (循環雙向鏈表)
1.1 簡介
Linux中的鏈表用法與一般數據結構書中介紹的用法有些不一樣。
Linux內核中,為了保證鏈表的通用性,將鏈表的節點結構單獨抽取了出來,也就是將鏈表的結構和鏈表的數據分開定義。
一般數據結構的書中介紹到的鏈表都是將鏈表的數據和鏈表的結構一起定義的。
注:具體介紹可以我之前的博客參見:http://www.cnblogs.com/wang_yb/archive/2013/04/16/3023892.html 中的 1.2節
里面很重要的一點就是:鏈表結構和數據分開后,是如何通過鏈表節點結構來獲取數據的?
帶有safe的函數或者宏都是可以用於多線程的
1.2 修改部分
- 刪除了hlist相關內容
- 修改了 list_del 函數: 將 LIST_POISON1 和 LIST_POISON2 改成了 NULL
- 刪除了 list_empty_careful: 用戶空間用不上
- 刪除 __list_for_each: 和 list_for_each 重復
- 刪除 list_prepare_entry: 暫時不需要
- 刪除 list_safe_reset_next: 暫時不需要
- 刪除 list_rotate_left: 暫時不需要
- 所有變量 new 改為了 newnode: new 是 c++ 關鍵字,用CppUTest進行測試時無法編譯
1.3 list.h 對外的接口
No. |
主要 函數 |
說明 |
1. | list_add | 在 head 之后追加一個節點 |
2. | list_add_tail | 在 head 之前追加一個節點, 也就是在末尾追加一個節點 |
3. | list_del | 刪除一個節點, 並將這個節點的next, prev 置為 NULL |
4. | list_del_init | 刪除一個節點並初始化刪除的節點 |
5. | list_replace | 替換一個節點 |
6. | list_replace_init | 替換一個節點, 並初始化被替換的節點 |
7. | list_move | 移動節點到 head 之后 |
8. | list_move_tail | 移動節點到 head 之前 |
9. | list_is_last | 判斷節點是否是鏈表中最后一個 |
10. | list_empty | 判斷鏈表是否為空 (即, 是否只有 head 節點) |
11. | list_is_singular | 判斷鏈表中是否只有一個節點 (除了 head 之外) |
12. | list_cut_position | 將1個鏈表截斷為2個鏈表 |
13. | list_splice | 將2個鏈表合並為1個鏈表, @list中的所有節點(不包括list)加入到 head 之后 |
14. | list_splice_tail | 將2個鏈表合並為1個鏈表, @list中的所有節點(不包括list)加入到 head 之前 |
15. | list_splice_init | 同 list_splice, 最后會初始化 @list |
16. | list_splice_tail_init | 同 list_splice_tail, 最后會初始化 @list |
No. |
主要 宏 |
說明 |
1. | list_entry | 獲取包含此節點的 struct |
2. | list_first_entry | 獲取包含此節點的 首個 struct |
3. | list_for_each | 從 head節點之后一個節點開始向后循環 |
4. | list_for_each_prev | 從 head節點之前一個節點開始向前循環 |
5. | list_for_each_safe | list_for_each 的安全版本, 即, 循環時即使有其它線程刪除節點也可正常運行 |
6. | list_for_each_prev_safe | list_for_each_prev 的安全版本 |
7. | list_for_each_entry | 同 list_for_each, 只是參數不同 |
8. | list_for_each_entry_reverse | 同 list_for_each_prev, 只是參數不同 |
9. | list_for_each_entry_continue | 同 list_for_each_entry, 但不是從頭(head)開始循環的 |
10. | list_for_each_entry_continue_reverse | 同 list_for_each_entry_reverse, 但不是從頭(head)開始循環的 |
11. | list_for_each_entry_from | 從指定位置開始向后循環 |
12. | list_for_each_entry_safe | list_for_each_entry 的安全版本 |
13. | list_for_each_entry_safe_continue | list_for_each_entry_continue 的安全版本 |
14. | list_for_each_entry_safe_from | list_for_each_entry_from 的安全版本 |
15. | list_for_each_entry_safe_reverse | list_for_each_entry_reverse 的安全版本 |
1.4 使用示例 - 測試 list.h 中所有的list操作
構造如下場景,用來測試上述列出的所有的 list 操作:
1. 構造用來測試的 struct:(為了使得測試結果一目了然,struct盡量簡單)
struct test_struct { int num; struct list_head head; };
2. 逐個函數進行測試,使用測試框架 cppUTest
3. 宏 相關的暫時沒有測試
4. 運行測試非常簡單(前提是得安裝 cpputest)
make ./test_list -v
2. rbtree 介紹
1.1 簡介
紅黑樹是一種自平衡的二叉搜索樹。紅黑樹是有序的。
注: 具體介紹可以我之前的博客參見:http://www.cnblogs.com/wang_yb/archive/2013/04/16/3023892.html 中的 第4節
這里只補充一點,紅黑樹雖然有些復雜,但是它的查找,插入,刪除操作的效率還不錯。查找,插入,刪除的時間復雜度都是O(log n) n是樹中元素數目
1.2 修改部分
為了是 rbtree 更加簡單,暫時刪除了以下內容:
- 刪除了函數指針的定義 typedef rb_augment_f
- 刪除了 rb_augment_insert
- 刪除了 rb_augment_erase_begin
- 刪除了 rb_augment_erase_end
- 刪除了 rb_link_node
1.3 rbtree.h 對外接口
注意: rbtree 的對外接口中沒有插入node的接口,只有在插入node之后改變node顏色的接口
可能是由於node的順序因具體struct而異,所以沒法統一實現
No. |
主要 函數 |
說明 |
1. | rb_set_parent | 設置父節點的地址 |
2. | rb_set_color | 設置節點顏色 |
3. | rb_init_node | 初始化節點 |
4. | rb_insert_color | 設置新插入節點的顏色 |
5. | rb_erase | 刪除一個節點 |
6. | rb_next | 返回當前節點的下一個節點 |
7. | rb_prev | 返回當前節點的上一個節點 |
8. | rb_first | 返回第一個葉子節點(也就是最左邊的葉子節點) |
9. | rb_last | 返回最后一個葉子節點(也就是最右邊的葉子節點) |
10. | rb_replace_node | 替換rbtree中的一個node(只是簡單的替換,沒有管替換的顏色對不對,數據的順序對不對) |
No. |
主要 宏 |
說明 |
1. | rb_parent | 獲取父節點的地址 |
2. | rb_color | 節點的顏色 |
3. | rb_is_red | 是否紅節點 |
4. | rb_is_black | 是否黑節點 |
5. | rb_set_red | 設置節點為紅色 |
6. | rb_set_black | 設置節點為黑色 |
7. | RB_ROOT | 初始化根節點 |
8. | rb_entry | 獲取包含rbtree node的struct |
9. | RB_EMPTY_ROOT | 判斷是否只有根節點 |
10. | RB_EMPTY_NODE | 判斷節點是否剛初始化,還沒有加到樹中 |
11. | RB_CLEAR_NODE | 設置節點的父節點也指向自己 |
1.4 rbtree.c 補充說明
rbtree.c 中函數都比較簡單,比較復雜的是 rb_insert_color 和 rb_erase
這2個函數還涉及其它未公開的函數 __rb_rotate_left, __rb_rotate_right, __rb_erase_color
1. __rb_rotate_left : 左旋,即,以參數 node 為中心點,逆時針旋轉。左旋可以調整右子樹的高度
下面的4副圖演示了左旋時,struct rb_node 的 left 和 right 指針的變化。
下圖是最復雜的一種情況,即所有相關節點的左右子樹不為空的情況
2. __rb_rotate_right : 右旋,即,以參數 node 為中心點,順時針旋轉。右旋可以調整左子樹的高度
下面的4副圖演示了右旋時,struct rb_node 的 left 和 right 指針的變化。
下圖是最復雜的一種情況,即所有相關節點的左右子樹不為空的情況
3. rb_erase : 刪除節點,調用 __rb_erase_color 調整顏色
下圖演示刪除節點時,struct rb_node 的 left 和 right 指針的變化。
下圖是最復雜的一種情況,即所有相關節點的左右子樹不為空的情況
4. __rb_erase_color : 刪除節點后,調整被刪除節點后節點的顏色
被刪除節點 A 的位置由被刪除節點的下一個節點 B(即被刪除節點的右子樹中最左的節點)替換。
調整的顏色就是 B 節點的 child 和 parent
刪除時各種情況的分析參見:http://zh.wikipedia.org/wiki/紅黑樹
5. rb_insert_color : 設置新插入節點的顏色,調整rbtree的平衡
插入的位置需要自己定義,這個函數只是調整插入后節點的顏色
插入時各種情況的分析參見:http://zh.wikipedia.org/wiki/紅黑樹
1.5 使用示例
構造如下場景,用來測試上述列出的所有的 rbtree 操作:
1. 構造用來測試的 struct:(為了使得測試結果一目了然,struct盡量簡單)
struct test_struct { int num;
struct rb_node node; };
2. 逐個函數進行測試,使用測試框架 cppUTest
3. 宏 相關的暫時沒有測試
4. 運行測試非常簡單(前提是得安裝 cpputest)
make ./test_rbtree -v