wake-up-neo.com

Container_of-Makro im Linux-Kernel verstehen

Beim Durchsuchen des Linux-Kernels habe ich ein container_of-Makro gefunden, das wie folgt definiert ist:

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

Ich verstehe, was container_of macht, aber was ich nicht verstehe, ist der letzte Satz 

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

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);

was aussieht als würde man nichts tun ... Könnte jemand bitte die Lücke hier füllen?

58
jaeyong

Ihr Anwendungsbeispiel container_of(dev, struct wifi_device, dev); ist möglicherweise etwas irreführend, da Sie zwei Namespaces dort mischen.

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

Höchstwahrscheinlich löst diese Vermischung alle diese Kopfschmerzen aus. Tatsächlich bezieht sich der member-Parameter in Ihrem Zitat auf den Namen, der diesem Member in der Containerstruktur gegeben wurde.

Nehmen Sie zum Beispiel diesen Container:

struct container {
  int some_other_data;
  int this_data;
}

Und einen Zeiger int *my_ptr auf das Mitglied this_data, mit dem Sie einen Makro auf struct container *my_container setzen, indem Sie Folgendes verwenden:

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

Um den korrekten Zeigerstandort zu erhalten, muss der Versatz von this_data zum Anfang der Struktur berücksichtigt werden.

Sie müssen lediglich den Versatz des Elements this_data von Ihrem Zeiger my_ptr abziehen, um die korrekte Position zu erhalten.

Genau das macht die letzte Zeile des Makros.

70
mikyra

Der letzte Satz:

(type *)(...)

ein Zeiger auf eine gegebene type. Der Zeiger wird als Versatz von einem gegebenen Zeiger berechnet: dev:

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

Wenn Sie das Makro cointainer_of verwenden, möchten Sie die Struktur abrufen, die den Zeiger eines bestimmten Felds 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 die Mitte einer Struktur zeigt (und Sie wissen, dass dies ein Zeiger auf das Feld two [ der Feldname in der Struktur ] ist), Sie möchten jedoch die gesamte Struktur (numbers) abrufen. So berechnen Sie den Versatz des Feldes two in der Struktur:

offsetof(type,member)

und subtrahieren Sie diesen Versatz vom angegebenen Zeiger. Das Ergebnis ist der Zeiger auf den Anfang der Struktur. Schließlich wandeln Sie diesen Zeiger auf den Strukturtyp, um eine gültige Variable zu erhalten.

15
Federico

Es ist eine Verwendung einer gcc-Erweiterung, die Ausdrücke statement . Wenn Sie das Makro als etwas sehen, das einen Wert zurückgibt, lautet die letzte Zeile:

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

Auf der verlinkten Seite finden Sie eine Erklärung der zusammengesetzten Anweisungen. Hier ist ein Beispiel :

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

Die Ausgabe ist

b 25

8
shodanex

Ein wenig realer Kontext sagt klarer, unter verwende einen rotschwarzen Baum als Beispiel. Dies ist die .__-Art, die ich container_of verstehe.

da Documentation/rbtree.txt besagt, enthält es im Linux-Kernel-Code keine Rb_node-Daten, sondern Einträge 

Datenknoten in einer Baumstruktur sind Strukturen, die eine Struktur enthalten rb_node member.

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

in derselben -Datei befindet sich ein Makro rb_entry, das als definiert ist

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

eindeutig ist rb_entry dasselbe wie container_of.

bei mm/mmap.c:299 in der Funktionsdefinition browse_rb wird rb_entry verwendet:

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 ist klar, in container_of(ptr, type, member),

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

was container_of tut, ist wie in diesem Beispiel 

  • gegebene Adresse von obj.member (hier obj.vm_rb), geben Sie die Adresse von obj zurück. 
  • da es sich bei einer Struktur um einen Block mit zusammenhängendem Speicher handelt, ist address von obj.vm_rb minus offset between the struct and member die Adresse des Containers.

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

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

mm/mmap.c:299 - Verwendung von rb_entry

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

Documentation/rbtree.txt: - Dokumentation eines rot-schwarzen Baums

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

P.S.

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

file:k bedeutet k-te Zeile in file.

1
qeatzy

conatainer_of () -Makro im Linux-Kernel -

Wenn Sie mehrere Datenstrukturen im Code verwalten möchten, müssen Sie fast immer eine Struktur in eine andere einbetten und diese jederzeit abrufen, ohne dass Fragen zu Speicherversätzen oder -grenzen gestellt werden. Nehmen wir an, Sie haben eine Strukturperson, wie hier definiert:

 struct person { 
     int age; 
     int salary;
     char *name; 
 } p;

Indem Sie lediglich einen Zeiger auf Alter oder Gehalt haben, können Sie die gesamte Struktur abrufen (die diesen Zeiger enthält). Wie der Name sagt, wird das Makro container_of verwendet, um den Container des angegebenen Felds einer Struktur zu finden. Das Makro ist in include/linux/kernel.h definiert und sieht folgendermaßen aus:

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

Fürchte dich nicht vor den Zeigern. sehe sie einfach wie folgt:

container_of(pointer, container_type, container_field); 

Hier sind die Elemente des vorhergehenden Codefragments:

  • zeiger: Dies ist der Zeiger auf das Feld in der Struktur
  • container_type: Dies ist der Typ der Struktur, die den Zeiger umschließt (enthält)
  • container_field: Dies ist der Name des Feldes, auf das der Zeiger innerhalb der Struktur zeigt

Betrachten wir den folgenden Container:

struct person { 
    int age; 
    int salary; 
    char *name; 
}; 

Betrachten wir nun eine der Instanzen zusammen mit einem Zeiger auf das Altersmitglied:

struct person somebody; 
[...] 
int *age_ptr = &somebody.age; 

Zusammen mit einem Zeiger auf den Namen member (age_ptr) können Sie das Makro container_of verwenden, um einen Zeiger auf die gesamte Struktur (den Container) zu erhalten, die diesen Member umgibt, indem Sie Folgendes verwenden:

struct person *the_person; 
the_person = container_of(age_ptr, struct person, age); 

container_of berücksichtigt den Altersversatz am Anfang der Struktur, um die korrekte Zeigerposition zu erhalten. Wenn Sie den Offset des Feldalters vom Zeiger age_ptr subtrahieren, erhalten Sie die korrekte Position. Das macht die letzte Zeile des Makros:

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

Wenn Sie dies auf ein reales Beispiel anwenden, erhalten Sie Folgendes:

struct family { 
    struct person *father; 
    struct person *mother; 
    int number_of_sons; 
    int family_id; 
} f; 

/*   
 * Fill and initialise f somewhere   */      [...]

 /* 
  * pointer to a field of the structure 
  * (could be any (non-pointer) member in the structure) 
  */ 
   int *fam_id_ptr = &f.family_id; 
   struct family *fam_ptr; 

   /* now let us retrieve back its family */ 
   fam_ptr = container_of(fam_id_ptr, struct family, family_id); 

Das Makro container_of wird hauptsächlich in generischen Containern im Kernel verwendet.

Das ist alles über container_of macro im Kernel.

1
Upen

Sehr nützlicher Link zum Verständnis von container_of macro im Linux-Kernel. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html

0
Anand Kumar