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?
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.
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.
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
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
obj.member
(hier obj.vm_rb
), geben Sie die Adresse von obj
zurück. 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
.
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:
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.
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