歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux雙向鏈表分析之list_del中的技巧

Linux雙向鏈表分析之list_del中的技巧

日期:2017/2/28 16:16:31   编辑:Linux教程

Linux內核的雙向鏈表是比較經典的東西,網上分析鏈表的同志基本分析了99%,就差了1%。那就是list_del函數。
先給出函數原型:

#ifdef CONFIG_ILLEGAL_POINTER_VALUE
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
#else
# define POISON_POINTER_DELTA 0
#endif

#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)

static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
集中體現在
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
這兩行代碼。
為什麼不寫成:
entry->next = NULL;
entry->prev = NULL;

我們先看一下相關的注釋:
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
翻譯一下:
在正常環境下,這個非空指針將會引起頁錯誤(通常所說的缺頁中斷)。可被用來驗證沒有初始化的鏈表節點。
首先,我們可以內核中找到一下函數:
這個函數是需要在打開了CONFIG_DEBUG_LIST這個宏後才起作用。很明顯這個宏是用於鏈表調試的。
void __list_del_entry(struct list_head *entry)
{
struct list_head *prev, *next;

prev = entry->prev;
next = entry->next;

if (WARN(next == LIST_POISON1,
"list_del corruption, %p->next is LIST_POISON1 (%p)\n",
entry, LIST_POISON1) ||
WARN(prev == LIST_POISON2,
"list_del corruption, %p->prev is LIST_POISON2 (%p)\n",
entry, LIST_POISON2) ||
WARN(prev->next != entry,
"list_del corruption. prev->next should be %p, "
"but was %p\n", entry, prev->next) ||
WARN(next->prev != entry,
"list_del corruption. next->prev should be %p, "
"but was %p\n", entry, next->prev))
return;

__list_del(prev, next);
}

這個地方有對這兩個宏的引用。這個函數的警告信息告訴我們這個節點已經被用過了,很顯然,我們將其與平常意義的節點作比較可以得到如下信息:

我們得到的這個鏈表是被刪掉的,不是剛初始化沒用的。

另外我大膽猜測了一下其另一個用途:
就算我們不打開CONFIG_DEBUG_LIST這個宏,我們在調試出錯代碼的時候經常會返回指針的值,我們很多時候出錯都是指針為NULL造成的,這時候我們如果發現是值是LIST_POISON1或LIST_POISON2的話,那我們馬上可以將注意力轉移到鏈表上來了,並且是因為誤用了一個已經從鏈表中刪除掉的節點。

Copyright © Linux教程網 All Rights Reserved