2014-03-28 55 views
3

爲什麼我們使用container_of宏?關於linux中container_of宏的疑問

container_of(pointer, container_type, container_field); 

據在於

「這個宏需要一個指針到名爲container_field字段,內 container_type類型的結構,並返回一個指針,指向包含 結構」的LDD說。

我的問題是:

  • 如果我們希望有一個指針,我們可以直接 分配結構(即CONTAINER_TYPE),對不對?
  • 那麼爲什麼其中一個字段的指針被用來爲整個結構分配一個 地址?
  • 有沒有人可以請示例說明使用宏的優勢 ?
+0

原因在於維護。如果每個人都使用這個宏,那麼只需編輯這個宏,就可以輕鬆地改變指針分配的方式。 –

回答

3

讓我給你舉個例子:

struct A 
{ 
    int some_data; 
    int other_data; 
}; 

現在讓我們假設有這樣的功能:

int get_some_data_from_other(int *other) 
{ 
    struct A *a = container_of(other, struct A, other_data); 
    return a->some_data; 
} 

正如你所看到的,我們能判斷出什麼是原始struct A包含給定int *other,通過知道指針所指向的結構的哪個字段。 這裏的關鍵是我們不要有一個引用結構本身,但只是一個指向其成員之一

這看起來很荒謬,但在一些非常聰明的結構中實際上是有用的。內核創建鏈表的方式就是一個很常見的例子。我建議你閱讀this post on kernelnewbies.org。讓我們來看看它的一個簡單的例子:

struct whatever 
{ 
    /* whatever data */ 
    struct list_head mylist; 
}; 

所以struct whatever有一些數據,但同時也希望作爲一個鏈表中的一個節點。他們在學校教你的是有一個不同的結構,其中包含next/prev指針以及指向struct whatever(或void *)的指針。這樣,您就擁有了通過其獲取數據的節點。

按照所有軟件工程標準,這實際上是一件好事。但是軟件工程標準對於效率並沒有多少關注。見Why should I have written ZeroMQ in C, not C++ (part II)。底線是,與傳統方法一樣,您必須與數據節點分開分配鏈接列表節點,即將內存分配,取消分配,碎片化和緩存未命中等開銷加倍。 Linux內核的做法恰恰相反。每個數據節點都包含一個通用鏈接列表節點。通用鏈接列表節點不知道有關數據的任何內容或分配方式,只知道如何連接到其他鏈接列表節點。

所以,讓我們深入瞭解一下:

+-------------------+  +---------------------+  +---------------------+ 
|     |  |      |  |      | 
| WHATEVER DATA |  | WHATEVER DATA 2 |  | WHATEVER DATA 3 | 
|     |  |      |  |      | 
|     |  |      |  |      | 
|     |  |      |  |      | 
|     |  |      |  |      | 
+-------------------+  +---------------------+  +---------------------+ 
|     |----->|      |----->|      | 
|  mylist  |  |  mylist 2  |  |  mylist 3  | 
|     |<-----|      |<-----|      | 
+-------------------+  +---------------------+  +---------------------+ 

你有什麼作爲鏈表都在裏面struct list_head指針指向其他struct list_head秒。請注意,他們不指向struct whatever,而是指向mylist裏面的這些結構。

假設你有一個節點,struct whatever w。你想找到下一個節點。你能做什麼?首先,您可以執行w.mylist.next以獲取指向下一個節點的mylist的指針。現在您必須能夠提取包含該節點的實際struct whatever。這就是container_of使用:

struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist); 

最後,要注意的是,Linux內核宏遍歷了一個鏈表的所有節點,這往往不是你想要的東西,所以你實際上並不需要直接使用container_of

1
Why do we use container_of macro ? 

宏的容器用於得到一個指向結構的開始,其通過類型包含元件。

例如

struct container { 
    int some_other_data; 
    int this_data; 
} 

和指針int *my_ptrthis_data會員你會使用宏來得到一個指針使用,以結構容器*my_container

struct container *my_container; 
my_container = container_of(my_ptr, struct container, this_data); 

考慮的this_data偏移到結構的開始考慮到獲取正確的指針位置至關重要。

實際上,您只需從指針my_ptr中減去成員this_data的偏移量即可獲取正確的位置。

另請參閱here,它清楚你的疑惑。

+0

'struct container * my_container'聲明本身將隱含指向結構權的開始?那麼爲什麼這個顯式聲明? – ddpd

+0

是的,但你可以減去成員的偏移量。 –

+0

@Dino'struct container * my_container'未初始化。它沒有指向任何地方。我們所有的是一個指向「int」的指針。如果我們知道int是'struct container'的成員,我們可以恢復一個指向'struct container'的指針。 – nos

0

是的,你可以使用寫你自己的演員/任務,但它有一些優勢。

效率 - 使用宏而不是簡單地聲明整個演員?

維護 - 如果每個人都用宏,你可以很容易地改變指針賦值完成後,在整個項目的方式,只需要編輯宏。

可讀性 - 什麼是簡單的閱讀?一個明確的演員或一個宏爲你做?

安全 - 你可以相信,這個宏的作品,它運行的任何演員和類型檢查是重要的,更好的,你自己的代碼。