2013-04-05 10 views
45

Als ich den Linux-Kernel blätterte, fand ich einen container_of Makro, das wie folgt definiert ist:Verständnis container_of Makro im Linux-Kernel

#define container_of(ptr, type, member) ({      \ 
     const typeof(((type *)0)->member) *__mptr = (ptr); \ 
     (type *)((char *)__mptr - offsetof(type,member));}) 

Ich verstehe, was zu tun tut container_of, aber was ich nicht verstehe, ist der letzte Satz, der

(type *)((char *)__mptr - offsetof(type,member));}) 

ist, wenn wir das Makro wie folgt verwenden:

container_of(dev, struct wifi_device, dev); 

Der entsprechende Teil des letzten Satzes wäre:

(struct wifi_device *)((char *)__mptr - offset(struct wifi_device, dev); 

, die wie nichts zu tun aussieht. Könnte jemand bitte hier die Lücke füllen?

+0

[diese Antwort] (https://stackoverflow.com/a/45923596/3625404) hat ** ein echtes und intuitives Beispiel ** mit dem rot-schwarzen Baum 'rb_node'. – qeatzy

Antwort

56

Ihr Verwendungsbeispiel container_of(dev, struct wifi_device, dev); könnte ein wenig irreführend sein, da Sie dort zwei Namespaces mischen.

Während sich die erste dev in Ihrem Beispiel auf den Namen des Zeigers bezieht, verweist die zweite dev auf den Namen eines Strukturelements.

Höchstwahrscheinlich provoziert dieser Mix alle diese Kopfschmerzen. In der Tat bezieht sich der Parameter member in Ihrem Zitat auf den Namen, der diesem Mitglied in der Containerstruktur gegeben wurde.

Unter diesen Behälter zum Beispiel:

struct container { 
    int some_other_data; 
    int this_data; 
} 

und einen Zeiger int *my_ptr zum this_data Mitglied Sie das Makro verwenden würde, einen Zeiger auf struct container *my_container zu erhalten, indem mit:

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

Unter den Offset von this_data an den Anfang der Struktur berücksichtigt werden, ist wesentlich, um die richtige Zeigerposition zu erhalten.

Effektiv müssen Sie nur den Offset des Mitglieds this_data von Ihrem Zeiger my_ptr subtrahieren, um den richtigen Standort zu erhalten.

Das ist genau das, was die letzte Zeile des Makros tut.

+3

Für diejenigen von Ihnen, die eine ausführlichere Erklärung benötigen: [Radek Pazdera] (https://twitter.com/radekpazdera) [erklärt] (http://radek.io/2012/11/10/magical-container_of-macro/) [container_of] (http://lxr.free-electronics.com/ident?i=container_of) Makro ('include/linux/kernel.h') wirklich klar auf [sein Blog] (http://radek.io /). [BTW] (http://www.tux.org/lkml/#contributors): [list_entry] Makro ('include/linux/list .h') wurde früher [sehr ähnlich] definiert (http://stackoverflow.com/questions/5550404/list-entry-in-linux), ist aber jetzt als "container_of" definiert. –

+1

Auch dieser Blogpost von Greg Kroah-Hartman könnte nützlich sein: http://www.kroah.com/log/linux/container_of.html – EFraim

14

Der letzte Satz Besetzung:

(type *)(...) 

ein Zeiger auf einen bestimmten type. Der Zeiger wird wie folgt berechnet aus einem bestimmten Zeiger Offset dev:

((char *)__mptr - offsetof(type,member)) 

Wenn Sie das cointainer_of Makro, können Sie die Struktur abrufen möchten, die den Zeiger eines bestimmten Feld enthält. Zum Beispiel:

struct numbers { 
    int one; 
    int two; 
    int three; 
} n; 

int *ptr = &n.two; 
struct numbers *n_ptr; 
n_ptr = container_of(ptr, struct numbers, two); 

Sie haben einen Zeiger, der in der Mitte einer Struktur zeigt (und Sie wissen, dass ein Zeiger auf die eingereichten two [den Feldnamen in der Struktur]), aber Sie wollen rufen Sie die gesamte Struktur ab (numbers). So berechnen Sie den Offset der eingereichten two in der Struktur:

offsetof(type,member) 

und subtrahieren diese von dem gegebenen Zeiger versetzt. Das Ergebnis ist der Zeiger auf den Anfang der Struktur. Abschließend wird dieser Zeiger auf den Strukturtyp umgesetzt, um eine gültige Variable zu erhalten.

6

Es ist eine Verwendung einer gcc-Erweiterung, die statements expressions. Wenn Sie das Makro als etwas Rückkehr Wert sehen, dann ist die letzte Zeile wäre:

return (struct wifi_device *)((char *)__mptr - offset(struct wifi_device, dev); 

die verknüpfte Seite für eine Erklärung der Verbindung Rechnung entnehmen. Hier ein Beispiel:

int main(int argc, char**argv) 
{ 
    int b; 
    b = 5; 
    b = ({int a; 
      a = b*b; 
      a;}); 
    printf("b %d\n", b); 
} 

Der Ausgang ist

b 25

0

Ein wenig realer Kontext sagt klarer, unter Verwendung rot-schwarz-Baum als Beispiel, die das ist Weise, die ich verstehe container_of.

als Documentation/rbtree.txt Staaten, in Code Linux-Kernel, es ist nicht Eintrag enthält Daten rb_node, eher

Datenknoten in einem rbtree Baum sind Strukturen ein Mitglied struct rb_node enthalten.

struct vm_area_struct (in Datei include/linux/mm_types.h:284) ist eine solche Struktur,

in derselben Datei gibt ein Makro rb_entry ist, die eindeutig als

#define rb_entry(ptr, type, member) container_of(ptr, type, member) 

definiert ist, ist rb_entry gleiche wie container_of.

bei mm/mmap.c:299 innen Funktionsdefinition browse_rb, gibt es eine Verwendung von rb_entry:

static int browse_rb(struct mm_struct *mm) 
{ 
    /* two line code not matter */ 
    struct rb_node *nd, *pn = NULL; /*nd, first arg, i.e. ptr. */ 
    unsigned long prev = 0, pend = 0; 

    for (nd = rb_first(root); nd; nd = rb_next(nd)) { 
     struct vm_area_struct *vma; 
     vma = rb_entry(nd, struct vm_area_struct, vm_rb); 
     /* -- usage of rb_entry (equivalent to container_of) */ 
     /* more code not matter here */ 

jetzt klar ist, in container_of(ptr, type, member),

  • type der Container-Struktur ist, hier struct vm_area_struct
  • member ist der Name eines Mitglieds von type Instanz, hier vm_rb, die rb_node vom Typ ist,
  • ptr ist ein Zeiger member ein type Beispiel zeigt, hier rb_node *nd.

was container_of tun ist, wie in diesem Beispiel

  • angegebene Adresse von obj.member (hier obj.vm_rb), kehren die Adresse obj.
  • da ein struct ist ein Block von zusammenhängendem Speicher, Adresse obj.vm_rb minus offset between the struct and member wird der Behälter Adresse sein.

include/linux/kernel.h:858 - Definition von container_of

include/linux/rbtree.h:51 - Definition von rb_entry

mm/mmap.c:299 - Nutzung von rb_entry

include/linux/mm_types.h:284 - struct vm_area_struct

Documentation/rbtree.txt: - Dokumentation der rot-schwarzen Baum

include/linux/rbtree.h:36 - Definition von struct rb_node

P. S.

Die obigen Dateien sind in der aktuellen Entwicklungsversion, d. H. 4.13.0-rc7.

file:k bedeuten kth Linie in file.