遍历Linux kernel的链表时删除元素的方法
內核的鏈表list_head設計相當巧妙。今天我說一下對list_head鏈表的遍歷時如何刪除元素。
鏈表遍歷時,如果刪除當前元素,一般都會出錯的。在由于語言的各種庫中的鏈表都是如此。list_head也一樣。
如,在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_DELTAlist_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_POISION1這個無意義的值了。
如果list是空的,那么pos=n后,仍然等于head,遍歷就此結束了!
因此,使用list_for_each_safe函數遍歷list_head鏈表,就可以安全地刪除當前元素了。
?
總結
以上是生活随笔為你收集整理的遍历Linux kernel的链表时删除元素的方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是野指针和内存泄露?如何避免野指针
- 下一篇: linux安装xgboost快速高效方法