了解Linux通用的双向循环链表.docx
- 文档编号:4180283
- 上传时间:2022-11-28
- 格式:DOCX
- 页数:6
- 大小:19.33KB
了解Linux通用的双向循环链表.docx
《了解Linux通用的双向循环链表.docx》由会员分享,可在线阅读,更多相关《了解Linux通用的双向循环链表.docx(6页珍藏版)》请在冰豆网上搜索。
了解Linux通用的双向循环链表
了解Linux通用的双向循环链表
在linux内核中,有一种通用的双向循环链表,构成了各种队列的基础。
链表的结构定义和相关函数均在include/linux/list.h中,下面就来全面的介绍这一链表的各种API。
structlist_head{structlist_head*next,*prev;};
这是链表的元素结构。
因为是循环链表,表头和表中节点都是这一结构。
有prev和next两个指针,分别指向链表中前一节点和后一节点。
/**Simpledoublylinkedlistimplementation.**SomeoftheinternalfuncTIons("__xxx")areusefulwhen*manipulaTIngwholelistsratherthansingleentries,as*someTImeswealreadyknowthenext/preventriesandwecan*generatebettercodebyusingthemdirectlyratherthan*usingthegenericsingle-entryroutines.*/#defineLIST_HEAD_INIT(name){list->prev=list;}
在初始化的时候,链表头的prev和next都是指向自身的。
/**Insertanewentrybetweentwoknownconsecutiveentries.**Thisisonlyforinternallistmanipulationwhereweknow*theprev/nextentriesalready!
*/#ifndefCONFIG_DEBUG_LISTstaticinlinevoid__list_add(structlist_head*new,structlist_head*prev,structlist_head*next){next->prev=new;new->next=next;new->prev=prev;prev->next=new;}#elseexternvoid__list_add(structlist_head*new,structlist_head*prev,structlist_head*next);#endif/***list_add-addanewentry*@new:
newentrytobeadded*@head:
listheadtoadditafter**Insertanewentryafterthespecifiedhead.*Thisisgoodforimplementingstacks.*/staticinlinevoidlist_add(structlist_head*new,structlist_head*head){__list_add(new,head,head->next);}/***list_add_tail-addanewentry*@new:
newentrytobeadded*@head:
listheadtoadditbefore**Insertanewentrybeforethespecifiedhead.*Thisisusefulforimplementingqueues.*/staticinlinevoidlist_add_tail(structlist_head*new,structlist_head*head){__list_add(new,head->prev,head);}
双向循环链表的实现,很少有例外情况,基本都可以用公共的方式来处理。
这里无论是加第一个节点,还是其它的节点,使用的方法都一样。
另外,链表API实现时大致都是分为两层:
一层外部的,如list_add、list_add_tail,用来消除一些例外情况,调用内部实现;一层是内部的,函数名前会加双下划线,如__list_add,往往是几个操作公共的部分,或者排除例外后的实现。
/**Deletealistentrybymakingtheprev/nextentries*pointtoeachother.**Thisisonlyforinternallistmanipulationwhereweknow*theprev/nextentriesalready!
*/staticinlinevoid__list_del(structlist_head*prev,structlist_head*next){next->prev=prev;prev->next=next;}/***list_del-deletesentryfromlist.*@entry:
theelementtodeletefromthelist.*Note:
list_empty()onentrydoesnotreturntrueafterthis,theentryis*inanundefinedstate.*/#ifndefCONFIG_DEBUG_LISTstaticinlinevoidlist_del(structlist_head*entry){__list_del(entry->prev,entry->next);entry->next=LIST_POISON1;entry->prev=LIST_POISON2;}#elseexternvoidlist_del(structlist_head*entry);#endif/***list_del_init-deletesentryfromlistandreinitializeit.*@entry:
theelementtodeletefromthelist.*/staticinlinevoidlist_del_init(structlist_head*entry){__list_del(entry->prev,entry->next);INIT_LIST_HEAD(entry);}
list_del是链表中节点的删除。
之所以在调用__list_del后又把被删除元素的next、prev指向特殊的LIST_POSITION1和LIST_POSITION2,是为了调试未定义的指针。
list_del_init则是删除节点后,随即把节点中指针再次初始化,这种删除方式更为实用。
/***list_replace-replaceoldentrybynewone*@old:
theelementtobereplaced*@new:
thenewelementtoinsert**If@oldwasempty,itwillbeoverwritten.*/staticinlinevoidlist_replace(structlist_head*old,structlist_head*new){new->next=old->next;new->next->prev=new;new->prev=old->prev;new->prev->next=new;}staticinlinevoidlist_replace_init(structlist_head*old,structlist_head*new){list_replace(old,new);INIT_LIST_HEAD(old);}
list_replace是将链表中一个节点old,替换为另一个节点new。
从实现来看,即使old所在地链表只有old一个节点,new也可以成功替换,这就是双向循环链表可怕的通用之处。
list_replace_init将被替换的old随即又初始化。
/***list_move-deletefromonelistandaddasanothershead*@list:
theentrytomove*@head:
theheadthatwillprecedeourentry*/staticinlinevoidlist_move(structlist_head*list,structlist_head*head){__list_del(list->prev,list->next);list_add(list,head);}/***list_move_tail-deletefromonelistandaddasanotherstail*@list:
theentrytomove*@head:
theheadthatwillfollowourentry*/staticinlinevoidlist_move_tail(structlist_head*list,structlist_head*head){__list_del(list->prev,list->next);list_add_tail(list,head);}
list_move的作用是把list节点从原链表中去除,并加入新的链表head中。
list_move_tail只在加入新链表时与list_move有所不同,list_move是加到head之后的链表头部,而list_move_tail是加到head之前的链表尾部。
/***list_is_last-testswhether@lististhelastentryinlist@head*@list:
theentrytotest*@head:
theheadofthelist*/staticinlineintlist_is_last(conststructlist_head*list,conststructlist_head*head){returnlist->next==head;}
list_is_last判断list是否处于head链表的尾部。
/***list_empty-testswhetheralistisempty*@head:
thelisttotest.*/staticinlineintlist_empty(conststructlist_head*head){returnhead->next==head;}/***list_empty_careful-testswhetheralistisemptyandnotbeingmodified*@head:
thelisttotest**Description:
*testswhetheralistisempty_and_checksthatnootherCPUmightbe*intheprocessofmodifyingeithermember(nextorprev)**NOTE:
usinglist_empty_careful()withoutsynchronization*canonlybesafeiftheonlyactivitythatcanhappen*tothelistentryislist_del_init().Eg.itcannotbeused*ifanotherCPUcouldre-list_add()it.*/staticinlineintlist_empty_careful(conststructlist_head*head){structlist_head*next=head->next;return(next==head)}
list_empty判断head链表是否为空,为空的意思就是只有一个链表头head。
list_empty_careful同样是判断head链表是否为空,只是检查更为严格。
/***list_is_singular-testswhetheralisthasjustoneentry.*@head:
thelisttotest.*/staticinlineintlist_is_singular(conststructlist_head*head){return!
list_empty(head)}
list_is_singular判断head中是否只有一个节点,即除链表头head外只有一个节点。
staticinlinevoid__list_cut_position(structlist_head*list,structlist_head*head,structlist_head*entry){structlist_head*new_first=entry->next;list->next=head->next;list->next->prev=list;list->prev=entry;entry->next=list;head->next=new_first;new_first->prev=head;}/***list_cut_position-cutalistintotwo*@list:
anewlisttoaddallremovedentries*@head:
alistwithentries*@entry:
anentrywithinhead,couldbetheheaditself*andifsowewontcutthelist**Thishelpermovestheinitialpartof@head,uptoand*including@entry,from@headto@list.Youshould*passon@entryanelementyouknowison@head.@list*shouldbeanemptylistoralistyoudonotcareabout*losingitsdata.**/staticinlinevoidlist_cut_position(structlist_head*list,structlist_head*head,structlist_head*entry){if(list_empty(head))return;if(list_is_singular(head)if(entry==head)INIT_LIST_HEAD(list);else__list_cut_position(list,head,entry);}
list_cut_position用于把head链表分为两个部分。
从head->next一直到entry被从head链表中删除,加入新的链表list。
新链表list应该是空的,或者原来的节点都可以被忽略掉。
可以看到,list_cut_position中排除了一些意外情况,保证调用__list_cut_position时至少有一个元素会被加入新链表。
staticinlinevoid__list_splice(conststructlist_head*list,structlist_head*prev,structlist_head*next){structlist_head*first=list->next;structlist_head*last=list->prev;first->prev=prev;prev->next=first;last->next=next;next->prev=last;}/***list_splice-jointwolists,thisisdesignedforstacks*@list:
thenewlisttoadd.*@head:
theplacetoadditinthefirstlist.*/staticinlinevoidlist_splice(conststructlist_head*list,structlist_head*head){if(!
list_empty(list))__list_splice(list,head,head->next);}/***list_splice_tail-jointwolists,eachlistbeingaqueue*@list:
thenewlisttoadd.*@head:
theplacetoadditinthefirstlist.*/staticinlinevoidlist_splice_tail(structlist_head*list,structlist_head*head){if(!
list_empty(list))__list_splice(list,head->prev,head);}
list_splice的功能和list_cut_position正相反,它合并两个链表。
list_splice把list链表中的节点加入head链表中。
在实际操作之前,要先判断list链表是否为空。
它保证调用__list_splice时list链表中至少有一个节点可以被合并到head链表中。
list_splice_tail只是在合并链表时插入的位置不同。
list_splice是把原来list链表中的节点全加到head链表的头部,而list_splice_tail则是把原来list链表中的节点全加到head链表的尾部。
/***list_splice_init-jointwolistsandreinitialisetheemptiedlist.*@list:
thenewlisttoadd.*@head:
theplacetoadditinthefirstlist.**Thelistat@listisreinitialised*/staticinlinevoidlist_splice_init(structlist_head*list,structlist_head*head){if(!
list_empty(list)){__list_splice(list,head,head->next);INIT_LIST_HEAD(list);}}/***list_splice_tail_init-jointwolistsandreinitialisetheemptiedlist*@list:
thenewlisttoadd.*@head:
theplacetoadditinthefirstlist.**Eachofthelistsisaqueue.*Thelistat@listisreinitialised*/staticinlinevoidlist_splice_tail_init(structlist_head*list,structlist_head*head){if(!
list_empty(list)){__list_splice(list,head->prev,head);INIT_LIST_HEAD(list);}}
list_splice_init除了完成list_splice的功能,还把变空了的list链表头重新初始化。
list_splice_tail_init除了完成list_splice_tail的功能,还吧变空了得list链表头重新初始化。
list操作的API大致如以上所列,包括链表节点添加与删除、节点从一个链表转移到另一个链表、链表中一个节点被替换为另一个节点、链表的合并与拆分、查看链表当前是否为空或者只有一个节点。
接下来,是操作链表遍历时的一些宏,我们也简单介绍一下。
/***list_entry-getthestructforthisentry*@ptr:
the">
list_entry主要用于从list节点查找其内嵌在的结构。
比如定义一个结构structA{structlist_headlist;};如果知道结构中链表的地址ptrList,就可以从ptrList进而获取整个结构的地址(即整个结构的指针)structA*ptrA=list_entry(ptrList,structA,list);这种地址翻译的技巧是linux的拿手好戏,container_of随处可见,只是链表节点多被封装在更复杂的结构中,使用专门的list_entry定义也是为了使用方便
/***list_first_entry-getthefirstelementfromalist*@ptr:
thelistheadtotaketheelementfrom.*@type:
thetypeofthestructthisisembeddedin.*@member:
thenameofthelist_structwithinthestruct.**Note,thatlistisexpectedtobenotempty.*/#definelist_first_entry(ptr,type,member)\list_entry((ptr)->next,type,member)
list_first_entry是将ptr看完一个链表的链表头,取出其中第一个节点对应的结构地址。
使用list_first_entry是应保证链表中至少有一个节点。
/***list_for_each-iterateoveralist*@pos:
theprefetch(pos->next),pos!
=(head);\pos=pos->next)
list_for_each循环遍历链表中的每个节点,从链表头部的第一个节点,一直到链表尾部。
中间的prefetch是为了利用平台特性加速链表遍历,在某些平台下定义为空,可以忽略
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 了解 Linux 通用 双向 循环