Container_of-Makro im Linux-Kernel verstehen

Lesezeit: 8 Minuten

Benutzeravatar von jaeyong
jaeyong

Als ich den Linux-Kernel durchsuchte, fand ich a container_of Makro, 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 tut, aber was ich nicht verstehe, ist der letzte Satz, der ist

(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 hier bitte jemand die Lücke füllen?

  • diese Antwort hat ein echtes und intuitives Beispiel mit rot-schwarzem Baum rb_node.

    – geil

    28. August 2017 um 16:36 Uhr

Benutzeravatar von mikyra
mikyra

Ihr Anwendungsbeispiel container_of(dev, struct wifi_device, dev); könnte etwas irreführend sein, da Sie dort zwei Namensräume mischen.

Während die erste dev In Ihrem Beispiel bezieht sich der Name des Zeigers auf die Sekunde dev bezieht sich auf den Namen eines Strukturmitglieds.

Höchstwahrscheinlich provoziert diese Verwechslung all diese Kopfschmerzen. Tatsächlich die member Der Parameter in Ihrem Zitat bezieht sich auf den Namen, der diesem Mitglied in der Containerstruktur gegeben wurde.

Nehmen wir zum Beispiel diesen Container:

struct container {
  int some_other_data;
  int this_data;
}

Und ein Zeiger int *my_ptr zum this_data Mitglied, auf das Sie das Makro verwenden würden, um einen Zeiger zu erhalten struct container *my_container durch die Nutzung:

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

Nehmen Sie den Offset von this_data bis zum Anfang der Struktur zu berücksichtigen, ist wichtig, um die richtige Zeigerposition zu erhalten.

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

Genau das macht die letzte Zeile des Makros.

  • Für diejenigen unter Ihnen, die eine detailliertere Erklärung benötigen: Radek Pazdera erklärt container_of Makro (include/linux/kernel.h) wirklich deutlich auf sein Blog. Übrigens: Listeneintrag Makro (include/linux/list.h) wurde früher sehr ähnlich definiert, ist jetzt aber definiert als container_of.

    – patryk.beza

    19. Februar 2016 um 17:55 Uhr

  • Auch dieser Blogpost von Greg Kroah-Hartman kann hilfreich sein: kroah.com/log/linux/container_of.html

    – Efraim

    28. Februar 2018 um 9:07 Uhr

  • Im Prinzip ist das eine nette Antwort, außer dass sie einen beträchtlichen Teil der Frage nicht wirklich beantwortet? Was bewirkt zB die letzte Codezeile und wie?

    – Michael Bier

    3. Januar 2019 um 12:24 Uhr

  • Wenn ich diesem Beispiel folge und GCC verwende, bekomme ich error: initialization from incompatible pointer type. Liegt das an der const in der Makrodefinition gemäß diesem Thread?stackoverflow.com/a/39963179/1256234

    – Andi J

    19. Juni 2020 um 5:05 Uhr


Federicos Benutzeravatar
Federico

Der letzte Satz gegossen:

(type *)(...)

ein Zeiger auf etwas Gegebenes type. Der Zeiger wird als Offset von einem gegebenen Zeiger berechnet dev:

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

Wenn Sie die verwenden cointainer_of -Makro 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 das ein Zeiger auf die Datei file ist two [the field name in the structure]), aber Sie möchten die gesamte Struktur abrufen (numbers). Sie berechnen also den Offset des Feldes two im Aufbau:

offsetof(type,member)

und diesen Offset von dem gegebenen Zeiger subtrahieren. Das Ergebnis ist der Zeiger auf den Anfang der Struktur. Schließlich wandeln Sie diesen Zeiger in den Strukturtyp um, um eine gültige Variable zu haben.

conatainer_of() Makro im Linux Kernel –

Wenn es darum geht, mehrere Datenstrukturen im Code zu verwalten, müssen Sie fast immer eine Struktur in eine andere einbetten und sie jederzeit abrufen, ohne dass Ihnen Fragen zu Speicheroffsets oder -grenzen gestellt werden. Angenommen, Sie haben eine Strukturperson, wie hier definiert:

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

Indem Sie nur einen Zeiger auf Alter oder Gehalt haben, können Sie die gesamte Struktur abrufen, die diesen Zeiger umschließt (enthält). Wie der Name schon 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 wie folgt aus:

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

Haben Sie keine Angst vor den Zeigern; sehen Sie 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 die Art der Strukturumhüllung (die den Zeiger 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 seiner Instanzen zusammen mit einem Zeiger auf das Altersmitglied:

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

Zusammen mit einem Zeiger auf das Namenselement (age_ptr) können Sie das Makro container_of verwenden, um einen Zeiger auf die gesamte Struktur (Container) zu erhalten, die dieses Element umschließt, indem Sie Folgendes verwenden:

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

container_of berücksichtigt den Altersoffset am Anfang der Struktur, um die richtige Zeigerposition zu erhalten. Wenn Sie den Offset des Felds age vom Zeiger age_ptr abziehen, erhalten Sie den richtigen Ort. Dies ist, was die letzte Zeile des Makros tut:

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

Wendet man dies auf ein reales Beispiel an, ergibt sich 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 das container_of-Makro im Kernel.

  • Dies ist eine viel bessere Erklärung, zumindest für einen Kernel-Noob wie mich. Also stimme dem zu.

    – König

    6. April 2021 um 9:29 Uhr

Benutzeravatar von shodanex
Schodanex

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

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

Siehe die verlinkte Seite für eine Erklärung zusammengesetzter 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

Ein wenig realer Kontext sagt unten klarer Verwenden Sie als Beispiel einen rot-schwarzen Baumso verstehe ich das container_of.

wie Documentation/rbtree.txt heißt es, im Linux-Kernel-Code enthält rb_node keine Dateneingabe, sondern

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

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

In der gleichen Datei gibt es ein Makro rb_entry was definiert ist als

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

deutlich, rb_entry ist gleich wie container_of.

bei mm/mmap.c:299 innerhalb der Funktionsdefinition browse_rbes gibt 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 ist es klar, in container_of(ptr, type, member),

  • type ist die Containerstruktur, hier struct vm_area_struct
  • member ist Name eines Mitglieds von type Beispiel hier vm_rbdas vom Typ ist rb_node,
  • ptr ist ein Zeiger, der zeigt member von einem type Beispiel hier rb_node *nd.

was container_of tun ist, wie in diesem Beispiel,

  • angegebene Adresse von obj.member (hier obj.vm_rb), gib die Adresse von zurück obj.
  • da eine Struktur ein Block zusammenhängenden Speichers ist, Adresse von obj.vm_rb Minus-
    offset between the struct and member wird die Adresse des Containers sein.

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

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

mm/mmap.c:299 — Benutzung von rb_entry

include/linux/mm_types.h:284struct vm_area_struct

Documentation/rbtree.txt: — Dokumentation des rot-schwarzen Baums

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

PS

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

file:k mittlere kte Zeile in file.

Benutzeravatar von Anand
Anand

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

Benutzeravatar von Alok Prasad
Alok Prasad

Die einfachste Implementierung des Makros Container _of ist unten. Es reduziert alle komplexen Überprüfungen des Typs und funktioniert

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) 

ptr gibt die Adresse des Mitglieds an und subtrahiert einfach die Offset-Differenz und Sie erhalten die Startadresse.

Beispielnutzung

struct sample {
    int mem1;
    char mem2;
    int mem3;
};
int main(void)
{

struct sample sample1;

printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);
printf("Address of Structure sample1 (container_of Method) = %p\n", 
                        container_of(&sample1.mem3, struct sample, mem3));

return 0;
}

1420990cookie-checkContainer_of-Makro im Linux-Kernel verstehen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy