Linux kernelのチェーンテーブルを巡回するときに要素を削除する方法
カーネルのチェーンテーブルlist_ヘッドのデザインはかなり巧みだ.今日はリストについてお話ししますheadチェーンテーブルの遍歴時に要素を削除する方法.
チェーンテーブルの遍歴は、現在の要素を削除すると一般的にエラーが発生します.すべての言語のさまざまなライブラリのチェーンテーブルがそうです.list_ヘッドも同じです.
たとえば、javaのループで現在の要素を削除すると、java.util.C o n c u r r r e ntModificationException例外が放出されます.
参照:『Javaで1つのコレクションの複数の要素を削除する方法』http://blog.csdn.net/shendl/archive/2007/12/28/1999907.aspx 一文.
リストの使用for_eachはチェーンテーブルを遍歴し、現在の要素を脱チェーンすると、システムは容赦なくcrashを落とす.何のヒントもありません.そのため、このようなバグは非常に位置決めしにくい.
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で現在の要素を削除すると、チェーンテーブルの次の要素が正しく見つかりません.
リストを巡回する場合は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パラメータが1つ増えた.このパラメータもlist_headタイプです.
次の要素を保存することで、現在の要素を安全に削除でき、後続の要素が見つからないことはありません.
ループが終了すると、posはposのnext要素ではなくn要素を指す.posが脱鎖した後、pos要素のnextは空のポインタ、またはLIST_POISO N 1という無意味な値です.
リストが空の場合、pos=nの後もheadに等しく、遍歴はこれで終わります!
, lisf_for_each_safe list_head , 。