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 tut, 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);
das sieht aus wie nichts zu tun. Könnte jemand bitte die Lücke hier füllen?
c
linux-kernel
c-preprocessor
jaeyong
quelle
quelle
rb_node
.Antworten:
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
dev
in Ihrem Beispiel auf den Namen des Zeigersdev
bezieht, bezieht sich der zweite auf den Namen eines Strukturelements.Höchstwahrscheinlich verursacht diese Verwechslung all diese Kopfschmerzen. Tatsächlich
member
bezieht sich der Parameter in Ihrem Zitat auf den Namen, der diesem Mitglied in der Container-Struktur gegeben wurde.Nehmen Sie diesen Container zum Beispiel:
struct container { int some_other_data; int this_data; }
Und ein Zeiger
int *my_ptr
auf dasthis_data
Mitglied, auf das Sie das Makro verwenden würden, um einen Zeiger zu erhalten,struct container *my_container
indem Sie Folgendes verwenden :struct container *my_container; my_container = container_of(my_ptr, struct container, this_data);
Die Berücksichtigung des Versatzes
this_data
zum Anfang der Struktur ist wichtig, um die richtige Zeigerposition zu erhalten.Tatsächlich müssen Sie nur den Versatz des Elements
this_data
von Ihrem Zeiger subtrahierenmy_ptr
, um die richtige Position zu erhalten.Genau das macht die letzte Zeile des Makros.
quelle
include/linux/kernel.h
include/linux/list.h
container_of
error: initialization from incompatible pointer type
. Liegt das an derconst
in der Makrodefinition gemäß diesem Thread? stackoverflow.com/a/39963179/1256234Der letzte Satz:
ein Zeiger auf eine gegebene
type
. Der Zeiger wird als Versatz zu einem bestimmten Zeiger berechnetdev
:( (char *)__mptr - offsetof(type,member) )
Wenn Sie das
cointainer_of
Makro 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
[ den Feldnamen in der Struktur ] ist), aber Sie möchten die gesamte Struktur abrufen (numbers
). Sie berechnen also den Versatz destwo
in 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.
quelle
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:
return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
Auf der verlinkten Seite finden Sie eine Erläuterung 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
quelle
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:
struct person { int age; int salary; char *name; } p;
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:
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
Hab keine Angst vor den Zeigern; sehen Sie sie einfach wie folgt:
Hier sind die Elemente des vorhergehenden Codefragments:
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 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:
(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 Makro im Kernel.
quelle
Ein kleiner realer Kontext sagt klarer, unten verwenden Sie einen rot-schwarzen Baum als Beispiel , wie ich es verstehe
container_of
.Als
Documentation/rbtree.txt
Zustände enthält der Linux-Kernel-Code nicht rb_node, sondern Dateneingabestruct vm_area_struct
(in Dateiinclude/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)
klar
rb_entry
ist das gleiche wiecontainer_of
.Bei der
mm/mmap.c:299
Definition der inneren Funktionbrowse_rb
wird Folgendes verwendetrb_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 hier die Containerstrukturstruct vm_area_struct
member
ist der Name einestype
Instanzmitglieds, hiervm_rb
vom Typrb_node
,ptr
ist hier ein Zeiger,member
der auf einetype
Instanz zeigtrb_node *nd
.Was
container_of
ist, wie in diesem Beispiel,obj.member
(hierobj.vm_rb
) an und geben Sie die Adresse von zurückobj
.obj.vm_rb
Minusoffset between the struct and member
die Adresse des Containers.include/linux/kernel.h:858
- Definition voncontainer_of
include/linux/rbtree.h:51
- Definition vonrb_entry
mm/mmap.c:299
-- Benutzung vonrb_entry
include/linux/mm_types.h:284
- -struct vm_area_struct
Documentation/rbtree.txt:
- Dokumentation des rot-schwarzen Baumesinclude/linux/rbtree.h:36
- Definition vonstruct rb_node
PS
Die oben genannten Dateien befinden sich in der aktuellen Entwicklungsversion, d
4.13.0-rc7
. H.file:k
mittlere k-te Zeile infile
.quelle
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
quelle
Die einfachste Implementierung des Makros Container _of ist unten aufgeführt. Es reduziert alle komplexen Überprüfungen von Typ und Funktion
#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 nur die Offsetdifferenz, und Sie erhalten die Startadresse.
Anwendungsbeispiel
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; }
quelle