最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

一文帶你了解!你不知道的Linux內(nèi)核中的算法和數(shù)據(jù)結(jié)構(gòu)!

2022-02-28 21:16 作者:補給站Linux內(nèi)核  | 我要投稿

測試方法準備

  • 由于需要在內(nèi)核中進行代碼測試驗證,完整編譯安裝內(nèi)核比較耗時耗力。準備采用module形式來驗證。

Makefile

linked-list.c

安裝module



查找安裝情況


執(zhí)行l(wèi)og

刪除module

鏈表、雙向鏈表、無鎖鏈表

  • 鏈表是一種常用的組織有序數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),它通過指針將一系列數(shù)據(jù)節(jié)點連接成一條數(shù)據(jù)鏈,是線性表的一種重要實現(xiàn)方式。相對于數(shù)組,鏈表具有更好的動態(tài)性,建立鏈表時無需預(yù)先知道數(shù)據(jù)總量,可以隨機分配空間,可以高效地在鏈表中的任意位置實時插入或刪除數(shù)據(jù)。鏈表的開銷主要是訪問的順序性和組織鏈的空間損失。

  • 通常鏈表數(shù)據(jù)結(jié)構(gòu)至少應(yīng)包含兩個域:數(shù)據(jù)域和指針域,數(shù)據(jù)域用于存儲數(shù)據(jù),指針域用于建立與下一個節(jié)點的聯(lián)系。按照指針域的組織以及各個節(jié)點之間的聯(lián)系形式,鏈表又可以分為單鏈表、雙鏈表、循環(huán)鏈表等多種類型.



  • 通過設(shè)計前驅(qū)和后繼兩個指針域,雙鏈表可以從兩個方向遍歷,這是它區(qū)別于單鏈表的地方。如果打亂前驅(qū)、后繼的依賴關(guān)系,就可以構(gòu)成"二叉樹";如果再讓首節(jié)點的前驅(qū)指向鏈表尾節(jié)點、尾節(jié)點的后繼指向首節(jié)點(如圖2中虛線部分),就構(gòu)成了循環(huán)鏈表;如果設(shè)計更多的指針域,就可以構(gòu)成各種復(fù)雜的樹狀數(shù)據(jù)結(jié)構(gòu)。



  • 循環(huán)鏈表的特點是尾節(jié)點的后繼指向首節(jié)點。前面已經(jīng)給出了雙循環(huán)鏈表的示意圖,它的特點是從任意一個節(jié)點出發(fā),沿兩個方向的任何一個,都能找到鏈表中的任意一個數(shù)據(jù)。如果去掉前驅(qū)指針,就是單循環(huán)鏈表。

Simple doubly linked list

數(shù)據(jù)結(jié)構(gòu):

聲明和初始化:

  • static inline void INIT_LIST_HEAD(struct list_head *list)

在表頭插入和在表尾插入:

  1. static inline void list_add(struct list_head *new, struct list_head *head)

  2. static inline void list_add_tail(struct list_head *entry, struct list_head *head)

  • 刪除,被刪除的節(jié)點prev、next分別被設(shè)為LIST_POISON2、LIST_POISON1,當(dāng)訪問此節(jié)點時會引起葉故障。保證不在鏈表中的節(jié)點項不可訪問。

  1. static inline void list_del(struct list_head *entry)

  2. static inline void list_del_init(struct list_head *entry) ?將entry從鏈表解下來,重新初始化,就可以訪問節(jié)點。

  • 將節(jié)點從一個鏈表搬移到另一個鏈表,根據(jù)插入表頭和表位分兩種:

  1. static inline void list_move(struct list_head *list, struct list_head *head)

  2. static inline void list_move_tail(struct list_head *list, struct list_head *head)

用新節(jié)點替換糾結(jié)點:

  • static inline void list_replace(struct list_head *old, struct list_head *new)

將list插入到head:

  1. static inline void list_splice(const struct list_head *list, struct list_head *head)

  2. static inline void list_splice_tail(struct list_head *list, struct list_head *head)

  3. static inline void list_splice_init(struct list_head *list, struct list_head *head) 將list設(shè)為空鏈表

  4. static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) 將list設(shè)為空鏈表



  • static inline void list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry)

遍歷宏:

  1. list_entry(ptr, type, member)

  2. list_first_entry(ptr, type, member)

  3. list_last_entry(ptr, type, member)

  4. list_next_entry(pos, member)

  5. list_prev_entry(pos, member)

  6. list_for_each(pos, head)

  7. list_for_each_prev(pos, head) 反向操作

  8. list_for_each_safe(pos, n, head) 安全操作

  9. list_for_each_entry(pos, head, member) 遍歷鏈表是獲取鏈表節(jié)點

  10. list_for_each_entry_safe(pos, n, head, member) 安全操作

  11. list_for_each_entry_reverse(pos, head, member) 反向操作

判斷鏈表是否為空:

  • static inline int list_empty(const struct list_head *head)

Doubly linked list with a single pointer list head

  • linux內(nèi)核里邊除了著名的list雙向循環(huán)鏈表以外,還有一個重要的數(shù)據(jù)結(jié)構(gòu),就是哈希鏈表。哈希鏈表也在很多重要的地方有所使用,比如linux內(nèi)核的dentry,進程查詢,文件系統(tǒng)等,可以說,弄明白hlist對于理解linux內(nèi)核具有重要的意義。

  • linux內(nèi)核的hash鏈表有兩個數(shù)據(jù)結(jié)構(gòu)組成,一個是hlist_head是hash表的表頭,一個是hlist_node是hash標(biāo)的后續(xù)節(jié)點。

  • 在使用的時候,一般定義一個struct hlist_head xxx[100]數(shù)組(100只是一個代表的數(shù)字,視具體情況而定),采取哈希函數(shù)來將鍵值與數(shù)組的對應(yīng)的地址聯(lián)系起來,如果出現(xiàn)沖突的話,就在hlist_head的后邊繼續(xù)添加。 hlist_head的成員first指針指向后續(xù)的第一個節(jié)點,如果哈希鏈表是空的話,就為NULL。

  • 為什么hlist_head不弄成雙向鏈表呢,因為為了節(jié)約空間,如果一個指針的話,一個哈希數(shù)組的空間消耗就會減半。

  • hlist_node的成員next指向后續(xù)的節(jié)點的地址,如果為空就是NULL,另一個成員pprev是二級指針,指向前一個節(jié)點的next成員的地址,如果前一個成員是hlist_head的話,pprev的值就是前一個的first指針的地址。


#define HLIST_HEAD(name) struct hlist_head name = { ?.first = NULL } ?定義并且初始化。 #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) 在定義之后,需要初始化,不然使用會導(dǎo)致錯誤。?

static inline void INIT_HLIST_NODE(struct hlist_node *h) 初始化node節(jié)點?

static inline int hlist_empty(const struct hlist_head *h) 判斷hash鏈表是否為空?

static inline void hlist_del(struct hlist_node *n) 刪除節(jié)點,并且將節(jié)點next、pprev指針修改為LIST_POSITION1和LIST_POSITION2。?

?static inline void hlist_del_init(struct hlist_node *n) 此種方法更安全,刪除然后再初始化節(jié)點。?

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) 將節(jié)點插入到hash鏈表的頭結(jié)點后邊。?

static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) 將一個節(jié)點插入到next前面。?

static inline void hlist_add_behind(struct hlist_node *n, struct hlist_node *prev) 將一個節(jié)點插入到prev后面。?

遍歷訪問節(jié)點:?

hlist_for_each(pos, head) hlist_for_each_safe(pos, n, head) #define hlist_entry(ptr, type, member) container_of(ptr,type,member)?

hlist_entry_safe(ptr, type, member)?

hlist_for_each_entry(pos, head, member)?

hlist_for_each_entry_safe(pos, n, head, member)

Lock-less NULL terminated single linked list

  • 數(shù)據(jù)結(jié)構(gòu)如下:

#define LLIST_HEAD(name)? ? struct llist_head name = LLIST_HEAD_INIT(name)?

static inline void init_llist_head(struct llist_head *list)?

llist_entry(ptr, type, member)?

llist_for_each(pos, node)?

static inline bool llist_empty(const struct llist_head *head)?

static inline struct llist_node *llist_next(struct llist_node *node)?

static inline bool llist_add(struct llist_node *new, struct llist_head *head)?

bool llist_add_batch(struct llist_node *new_first, struct llist_node *new_last, struct llist_head *head)?

static inline struct llist_node *llist_del_all(struct llist_head *head)?

struct llist_node *llist_del_first(struct llist_head *head)

  • llist_add、llist_add_batch、llist_del_first都是基于cmpxchg原子操作來實現(xiàn),整個操作是原子的;llist_del_all是基于xchg來實現(xiàn)的。


  • cmpxchg(void* ptr, int old, int new),如果ptr和old的值一樣,則把new寫到ptr內(nèi)存,否則返回ptr的值,整個操作是原子的。在Intel平臺下,會用lock cmpxchg來實現(xiàn),這里的lock個人理解是鎖住內(nèi)存總線,這樣如果有另一個線程想訪問ptr的內(nèi)存,就會被block住。

B+樹


A relatively simple B+Tree implementation. I have written it as a learning exercise to understand how B+Trees work. Turned out to be useful as well.?

...?

A tricks was used that is not commonly found in textbooks. The lowest values are to the right, not to the left. All used slots within a node are on the left, all unused slots contain NUL values. Most operations simply loop once over all slots and terminate on the first NUL.

B樹誕生的背景:


在大規(guī)模數(shù)據(jù)存儲中,實現(xiàn)索引查詢這樣一個實際背景下,樹節(jié)點存儲的元素數(shù)量是有限的,這樣就會導(dǎo)致二叉樹結(jié)構(gòu)由于樹的深度過大而造成磁盤I/O讀寫過于頻繁,進而導(dǎo)致查詢效率低下。?

那么如何減少樹的深度,一個基本的想法是采用多叉樹結(jié)構(gòu)。

因為磁盤的操作費時費資源,那么如何提高效率,即如何避免頻繁的讀取呢?根據(jù)磁盤查找存取的次數(shù)往往由樹的高度決定,所以只要通過較好的結(jié)構(gòu)降低樹的高度。根據(jù)平衡二叉樹的啟發(fā),自然就想到平衡多叉樹結(jié)構(gòu)。

幾個算法時間復(fù)雜度度量:


O(n) 表示某函數(shù)值(未列出)是 n 的常數(shù)倍;亦即他們增長的速度相當(dāng).稱 大O,big O (發(fā)音 "歐" 英文字母 O ) 同理:O(logN):是 logN 的常數(shù)倍;O(nlogn):是 nlogn 的常數(shù)倍

優(yōu)先排序列表

  • plist有兩個重要結(jié)構(gòu)體struct plist_head和struct plist_node,分別用來表示plist表頭和plist節(jié)點。

相關(guān)函數(shù):

  1. PLIST_HEAD(head) 初始化plist表頭

  2. PLIST_NODE_INIT(node, __prio) 初始化plist節(jié)點

  3. static inline void plist_head_init(struct plist_head *head) 初始化plist表頭

  4. static inline void plist_node_init(struct plist_node *node, int prio) 初始化plist節(jié)點

添加節(jié)點、刪除節(jié)點:

  1. extern void plist_add(struct plist_node *node, struct plist_head *head); 通過plist_add添加到head的node是按照prio優(yōu)先級由高到低順序在node_list上排列。

  2. extern void plist_del(struct plist_node *node, struct plist_head *head);

  3. extern void plist_requeue(struct plist_node *node, struct plist_head *head); 是plist_del的優(yōu)化版本

遍歷plist:

  • plist_for_each(pos, head)

判斷head是否為空:

  • static inline int plist_head_empty(const struct plist_head *head)

判斷當(dāng)前node是否在node_list上:

  • static inline int plist_node_empty(const struct plist_node *node)

獲取前一、后一節(jié)點:

  1. plist_next(pos)

  2. plist_prev(pos)

獲取首節(jié)點、尾節(jié)點:

  1. static inline struct plist_node *plist_first(const struct plist_head *head)

  2. static inline struct plist_node *plist_last(const struct plist_head *head)

下面是對plist進行的一些驗證:

通過初始化不超過10個node節(jié)點,優(yōu)先級為0-9。然后查看node_list和prio_list兩鏈表的節(jié)點情況:

[22050.404475] start plist test?

[22050.404481] dump_list start?

[22050.404482] node_list: 0 0 1 1 2 6 8 8 9 9?

[22050.404486] prio_list: 0 1 2 6 8 9?

[22050.404488] MIN(prio)=0 MAX(prio)=9?

[22050.404489] dump_list end?

[22050.404491] end plist test?

[22050.947810] start plist test?

[22050.947816] dump_list start?

[22050.947817] node_list: 0 1 1 2 2 3 3 3 8 8?

[22050.947820] prio_list: 0 1 2 3 8?

[22050.947822] MIN(prio)=0 MAX(prio)=8?

[22050.947823] dump_list end?

[22050.947825] end plist test?

[22051.491245] start plist test?

[22051.491254] dump_list start?

[22051.491256] node_list: 0 1 2 3 3 3 6 9 9 9?

[22051.491262] prio_list: 0 1 2 3 6 9?

[22051.491266] MIN(prio)=0 MAX(prio)=9?

[22051.491267] dump_list end?

[22051.491271] end plist test

可以看出node_list上的節(jié)點按照優(yōu)先級由高到低排序,優(yōu)先級可能會重復(fù);在prio_list上是不同優(yōu)先級的節(jié)點。如下所示:


* pl:prio_list (only for plist_node)?

* nl:node_list?

*HEAD| ? ? ? ? ? ? NODE(S)?

* ? ? ? ?|?

* ? ? ? ?||------------------------------------|?

* ? ? ? ?||->|pl|<->|pl|<--------------->|pl|<-|?

* ? ? ? ?| ? ? |10| ? |21| ? |21| ? |21| ? |40| ? (prio)?

* ? ? ? ?| ? ? | ?| ? | ?| ? | ?| ? | ?| ? | ?|?

* ? ? ? ?| ? ? | ?| ? | ?| ? | ?| ? | ?| ? | ?|?

* ? ? ? ?| ->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-|?

* ? ? ? ?|-------------------------------------------------|


一文帶你了解!你不知道的Linux內(nèi)核中的算法和數(shù)據(jù)結(jié)構(gòu)!的評論 (共 條)

分享到微博請遵守國家法律
安福县| 得荣县| 板桥市| 阿克苏市| 会泽县| 彭水| 客服| 财经| 页游| 祁连县| 宁蒗| 汉阴县| 丰原市| 丰台区| 星座| 新建县| 昌都县| 仁布县| 嘉黎县| 宜川县| 荥经县| 信阳市| 闽侯县| 西峡县| 丹阳市| 岳西县| 安阳市| 濉溪县| 金乡县| 石首市| 海安县| 长宁区| 全椒县| 双峰县| 化德县| 麻栗坡县| 峡江县| 监利县| 永川市| 萝北县| 江源县|