Grundlegendes zum Makro container_of im Linux-Kernel

82

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

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

Wenn wir das Makro wie folgt verwenden:

Der entsprechende Teil des letzten Satzes wäre:

das sieht aus wie nichts zu tun. Könnte jemand bitte die Lücke hier füllen?

jaeyong
quelle
Diese Antwort enthält ein reales und intuitives Beispiel für die Verwendung eines rot-schwarzen Baums rb_node.
Qeatzy

Antworten:

86

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

Während sich der erste devin Ihrem Beispiel auf den Namen des Zeigers devbezieht, bezieht sich der zweite auf den Namen eines Strukturelements.

Höchstwahrscheinlich verursacht diese Verwechslung all diese Kopfschmerzen. Tatsächlich memberbezieht sich der Parameter in Ihrem Zitat auf den Namen, der diesem Mitglied in der Container-Struktur gegeben wurde.

Nehmen Sie diesen Container zum Beispiel:

Und ein Zeiger int *my_ptrauf das this_dataMitglied, auf das Sie das Makro verwenden würden, um einen Zeiger zu erhalten, struct container *my_containerindem Sie Folgendes verwenden :

Die Berücksichtigung des Versatzes this_datazum Anfang der Struktur ist wichtig, um die richtige Zeigerposition zu erhalten.

Tatsächlich müssen Sie nur den Versatz des Elements this_datavon Ihrem Zeiger subtrahieren my_ptr, um die richtige Position zu erhalten.

Genau das macht die letzte Zeile des Makros.

Mikyra
quelle
5
Für diejenigen unter Ihnen, die eine ausführlichere Erklärung benötigen: Radek Pazdera hat container_of macro ( ) in seinem Blog sehr deutlich erklärt . BTW : list_entry Makro ( ) verwendet , um definiert werden sehr ähnlich , aber jetzt ist definiert als . include/linux/kernel.hinclude/linux/list.hcontainer_of
patryk.beza
1
Auch dieser Blogpost von Greg Kroah-Hartman kann nützlich sein: kroah.com/log/linux/container_of.html
EFraim
1
Im Prinzip ist das eine nette Antwort, außer dass sie nicht wirklich einen wesentlichen Teil der Frage beantwortet? Was erreicht beispielsweise die letzte Codezeile und wie?
Michael Beer
Wenn ich diesem Beispiel folge und GCC verwende, erhalte ich error: initialization from incompatible pointer type. Liegt das an der constin der Makrodefinition gemäß diesem Thread? stackoverflow.com/a/39963179/1256234
Andy J
18

Der letzte Satz:

ein Zeiger auf eine gegebene type. Der Zeiger wird als Versatz zu einem bestimmten Zeiger berechnet dev:

Wenn Sie das cointainer_ofMakro verwenden, möchten Sie die Struktur abrufen, die den Zeiger eines bestimmten Felds enthält. Zum Beispiel:

Sie haben einen Zeiger, der in die Mitte einer Struktur zeigt (und Sie wissen, dass dies ein Zeiger auf das Feldtwo [ den Feldnamen in der Struktur ] ist), aber Sie möchten die gesamte Struktur abrufen ( numbers). Sie berechnen also den Versatz des twoin der Struktur abgelegten :

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, um eine gültige Variable zu erhalten.

Federico
quelle
10

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

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

Die Ausgabe ist

b 25

Shodanex
quelle
7

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 jederzeit abrufen, ohne Fragen zu Speicherversätzen oder -grenzen zu erhalten. Angenommen, Sie haben eine Strukturperson, wie hier definiert:

Wenn 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 folgendermaßen aus:

Hab keine Angst vor den Zeigern; sehen Sie sie einfach wie folgt:

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 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:

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

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:

container_of berücksichtigt den Versatz des Alters am Anfang der Struktur, um die richtige Zeigerposition zu erhalten. Wenn Sie den Versatz des Feldalters vom Zeiger age_ptr subtrahieren, erhalten Sie die richtige Position. Dies ist, was die letzte Zeile des Makros tut:

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

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

Das ist alles über container_of Makro im Kernel.

Spyder
quelle
2

Ein kleiner realer Kontext sagt klarer, unten verwenden Sie einen rot-schwarzen Baum als Beispiel , wie ich es verstehe container_of.

Als Documentation/rbtree.txtZustände enthält der Linux-Kernel-Code nicht rb_node, sondern Dateneingabe

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

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

In derselben Datei befindet sich ein Makro, rb_entrydas als definiert ist

klar rb_entryist das gleiche wie container_of.

Bei der mm/mmap.c:299Definition der inneren Funktion browse_rbwird Folgendes verwendet rb_entry:

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

  • type ist hier die Containerstruktur struct vm_area_struct
  • memberist der Name eines typeInstanzmitglieds, hier vm_rbvom Typ rb_node,
  • ptrist hier ein Zeiger, memberder auf eine typeInstanz zeigt rb_node *nd.

Was container_ofist, wie in diesem Beispiel,

  • Geben Sie die Adresse von obj.member(hier obj.vm_rb) an und geben Sie die Adresse von zurück obj.
  • Da eine Struktur ein Block zusammenhängenden Speichers ist, ist die Adresse obj.vm_rbMinus offset between the struct and memberdie 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 -- Benutzung von rb_entry

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

Documentation/rbtree.txt: - Dokumentation des rot-schwarzen Baumes

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

PS

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

file:kmittlere k-te Zeile in file.

qeatzy
quelle
0

Die einfachste Implementierung des Makros Container _of ist unten aufgeführt. Es reduziert alle komplexen Überprüfungen von Typ und Funktion

ptr gibt die Adresse des Mitglieds an und subtrahiert nur die Offsetdifferenz, und Sie erhalten die Startadresse.

Anwendungsbeispiel

Alok Prasad
quelle