生活随笔
收集整理的這篇文章主要介紹了
Linux驱动编程 step-by-step (十一)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Linux 內(nèi)核鏈表(2)
之前描述了如何創(chuàng)建內(nèi)核鏈表(INIT_LIST_HEAD)向鏈表中添加節(jié)點(list_add)刪除一個鏈表節(jié)點(list_del)獲取一個鏈表節(jié)點對應(yīng)的結(jié)構(gòu)體(list_entry)等
接下來會介紹幾種操作
替換一個鏈表節(jié)點,合并兩個鏈表,將一個鏈表分成兩段,遍歷鏈表。
替換鏈表節(jié)點
替換節(jié)點很好理解,就是將新的節(jié)點替換老節(jié)點,將新的節(jié)點的對應(yīng)的prev,next指針指向老節(jié)點的prev,next。后將老節(jié)點prev->next指向新節(jié)點,老節(jié)點的next->prev指向新節(jié)點
[cpp]?view plaincopyprint?
static?inline?void?list_replace(struct?list_head?*old,?? ????????????????struct?list_head?*new)??????????????????????????????????????????????? {?? ????new->next?=?old->next;?????? ????new->next->prev?=?new;?????? ????new->prev?=?old->prev;?????? ????new->prev->next?=?new;?????? }??
此時old節(jié)點的next 與prev指針還是指向 原來的節(jié)點,可以使用以下函數(shù)重新初始化old節(jié)點使其指向 他自身
[cpp]?view plaincopyprint?
static?inline?void?list_replace_init(struct?list_head?*old,?? ????????????????????struct?list_head?*new)?? {?? ????list_replace(old,?new);?? ????INIT_LIST_HEAD(old);?? }??
...
更為一般的情況:我們替換鏈表節(jié)點大多都在鏈表頭或者鏈表尾,所以就有了以下函數(shù)
[cpp]?view plaincopyprint?
static?inline?void?list_move(struct?list_head?*list,?struct?list_head?*head)??
替換鏈表頭
[cpp]?view plaincopyprint?
static?inline?void?list_move_tail(struct?list_head?*list,?? ??????????????????struct?list_head?*head)??
替換鏈表尾
鏈表的判斷
有時候我們需要判斷鏈表是否為空,或者是否已經(jīng)到了鏈表的末尾
內(nèi)核鏈表已經(jīng)為我們實現(xiàn)了這些判斷函數(shù)。
檢查鏈表是否為空
[cpp]?view plaincopyprint?
static?inline?int?list_empty(const?struct?list_head?*head)?? {?? ????return?head->next?==?head;?? }??
類似的檢查是否已經(jīng)到了鏈表尾部函數(shù):
[cpp]?view plaincopyprint?
static?inline?int?list_is_last(const?struct?list_head?*list,?? ????????????????const?struct?list_head?*head)??
此外還有一個檢測鏈表是否為空的函數(shù)
[cpp]?view plaincopyprint?
static?inline?int?list_empty_careful(const?struct?list_head?*head)??
檢查鏈表為空,并且沒有另外的處理器回去操作它
合并兩個鏈表
合并鏈表跟添加一個鏈表節(jié)點差不多,在鏈表頭或鏈表尾添加新鏈表均會調(diào)用到__list_splice
[cpp]?view plaincopyprint?
static?inline?void?__list_splice(const?struct?list_head?*list,?? ?????????????????struct?list_head?*prev,?? ?????????????????struct?list_head?*next)?? {?? ????struct?list_head?*first?=?list->next;?? ????struct?list_head?*last?=?list->prev;?? ?? ????first->prev?=?prev;?? ????prev->next?=?first;?? ?? ????last->next?=?next;?? ????next->prev?=?last;?? }??
即將list 鏈表加到 prev 與 next之間
在鏈表頭添加新鏈表:
[cpp]?view plaincopyprint?
static?inline?void?list_splice(const?struct?list_head?*list,?? ????????????????struct?list_head?*head)??
在鏈表尾部添加新鏈表調(diào)用:
[cpp]?view plaincopyprint?
static?inline?void?list_splice_tail(struct?list_head?*list,?? ????????????????struct?list_head?*head)??
拆分一個鏈表
[cpp]?view plaincopyprint?
static?inline?void?list_cut_position(struct?list_head?*list,?? ????????struct?list_head?*head,?struct?list_head?*entry)??
以entry為節(jié)點拆分以head為頭的鏈表,拆分后list保存從head到entry的鏈表。head報鏈表是從entry->next 到鏈表尾。
遍歷鏈表
創(chuàng)建的鏈表的目的是為了能夠遍歷鏈表得到鏈表結(jié)構(gòu)中的有效數(shù)據(jù)。
上一篇文中提到 list_entry他只能獲得一個鏈表節(jié)點對應(yīng)的結(jié)構(gòu)體。
我們可以自己使用for 循環(huán)來遍歷鏈表:
[cpp]?view plaincopyprint?
for?(pos?=?head->next;?pos?!=?head;?pos?=?pos->next){?? ?????struct?data_struct?*data?=?list_entry(pos,?struct?data_struct,?list);?? ?????...?? }??
當然內(nèi)核已經(jīng)為我們提供了一套接口(本質(zhì)就是上邊的 for循環(huán))
[cpp]?view plaincopyprint?
list_for_each(pos,?head)??
到這里 我們想要一個更簡單的: 在循環(huán)的同時 就用list_entry為我們拿到鏈表節(jié)點對應(yīng)的數(shù)據(jù)結(jié)構(gòu)體。所以內(nèi)核工程師給我們一個接口:
[cpp]?view plaincopyprint?
list_for_each_entry(pos,?head,?member)??
pos:是數(shù)據(jù)結(jié)構(gòu)體指針, head是鏈表頭,member指在數(shù)據(jù)結(jié)構(gòu)中鏈表成員的名字
例如現(xiàn)在定義?
[cpp]?view plaincopyprint?
struct?data_struct{?? ????struct?list_head?list;?? ????Data?data;?? };??
我們要遍歷鏈表獲得 此結(jié)構(gòu)
[cpp]?view plaincopyprint?
{?? ????...?? ????struct?data_struct?*pdata;?? ????list_for_each_entry(pdata,?head,?list){?? ?????????Data?tmp?=?pdata->data;?? ?????????....?? ????}?? }??
在這些版本的遍歷中我們不能在循環(huán)內(nèi)刪除節(jié)點,如果有刪除操作怎會導(dǎo)致內(nèi)核崩潰(因為刪除節(jié)點時候 node->next被置為了空,如果進行操作.....),但有時候需要在遍歷過程中刪除節(jié)點,所以內(nèi)核NB工程師幫我們做了一個 for_safe的版本,他保存一個節(jié)點的副本,在節(jié)點被刪除后,副本仍有效。
[cpp]?view plaincopyprint?
list_for_each_safe(pos,?n,?head)??
[cpp]?view plaincopyprint?
list_for_each_entry_safe(pos,?n,?head,?member)??
此處 n用來保存副本
此外還有逆序遍歷等再次不再贅述……
請自行查看include/linux/list.h
總結(jié)
以上是生活随笔為你收集整理的Linux驱动编程 step-by-step (十一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。