遍历Linux kernel的链表时删除元素的方法
来源:互联网 发布:手机淘宝怎么找回密码 编辑:程序博客网 时间:2024/06/15 23:45
<!--@page { margin: 2cm }PRE.cjk { font-family: "DejaVu Sans", monospace }P { margin-bottom: 0.21cm }A:link { so-language: zxx }-->
内核的链表list_head设计相当巧妙。今天我说一下对list_head链表的遍历时如何删除元素。
链表遍历时,如果删除当前元素,一般都是会出错的。在所有语言的各种库中的链表都是如此。list_head也一样。
<!--@page { margin: 2cm }P { margin-bottom: 0.21cm }A:link { so-language: zxx }-->
如,在java的遍历中删除当前元素,会抛出java.util.ConcurrentModificationException异常。
见:《Java中如何删除一个集合中的多个元素》http://blog.csdn.net/shendl/archive/2007/12/28/1999907.aspx一文。
使用list_for_each遍历链表,如果使当前元素脱链,那么系统就会毫不留情的crash掉。什么提示信息都没有。因此这类bug非常难以定位。
list_for_each源码:
/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */#define list_for_each(pos, head) / for (pos = (head)->next; prefetch(pos->next), pos != (head); / pos = pos->next)
list_del脱链元素后,会把next和prev分别赋值为:
/* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA
list_del_init脱链元素后,会把next和prev都设置为自己。
因此,在list_for_each中删除当前元素后,就无法正确找到链表的下一个元素。
如果要在遍历list_head链表时,删除当前元素,那么就必须使用list_for_each_safe函数而不能使用list_for_each函数。
list_for_each_safe源码:
/** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */#define list_for_each_safe(pos, n, head) / for (pos = (head)->next, n = pos->next; pos != (head); / pos = n, n = pos->next)
这个函数比list_for_each函数多了一个n参数。这个参数也是list_head类型的。
它保存下一个元素,这样就可以安全的删除当前元素,不会造成找不到后续元素的情况发生。
在循环结束时,pos指向n元素,而不是指向pos的next元素。因为pos脱链后,pos元素的next可能已经是空指针,或者是LIST_POISON1这个无意义的值了。
如果list是空的,那么pos=n后,仍然等于head,遍历就此结束了!
因此,使用lisf_for_each_safe函数遍历list_head链表,就可以安全地删除当前元素了。
- 遍历Linux kernel的链表时删除元素的方法
- 遍历Linux kernel的链表时删除节点的方法 list_for_each_safe
- 遍历删除map中元素的方法
- 遍历数组删除某元素的方法
- Java中遍历Map时删除元素的方法
- STL中用erase()方法遍历删除元素注意的问题
- ArrayList边遍历自身边删除元素的方法
- 遍历List集合同时删除元素的正确方法
- 遍历map时删除不需要的元素方法
- C++ STL容器遍历删除元素的方法
- C#遍历List并删除元素的方法概述
- Map遍历的两种方式(首推entrySet())--遍历Map时删除元素的方法
- c++ 遍历map的时候删除元素
- Java_List元素的遍历和删除
- 循环遍历中删除集合的元素
- 遍历可变数组的同时删除元素
- iOS 数组遍历删除元素的问题
- map的遍历及删除元素
- 同个IP绑定多个虚拟主机 2003搭建
- adobe cs 彻底删除
- web编辑器
- 《深入浅出 Java Concurrency》—锁机制(一)Lock与ReentrantLock
- 《深入浅出 Java Concurrency》—锁机制(二) AQS
- 遍历Linux kernel的链表时删除元素的方法
- 《深入浅出 Java Concurrency》—锁机制(三) 加锁的原理 (Lock.lock)
- 《深入浅出 Java Concurrency》—锁机制(四) 锁释放与条件变量 (Lock.unlock And Condition)
- 《深入浅出 Java Concurrency》—锁机制(五) 闭锁 (CountDownLatch)
- eclipse打开main.xml就关闭的现象解决方案
- java中的动态代理》》》》》
- 农夫和狗
- 《深入浅出 Java Concurrency》—锁机制(六) CyclicBarrier
- 《深入浅出 Java Concurrency》—锁机制(七) 信号量 (Semaphore)