1. Wie oben erwähnt Ich habe dieses Makro gesehen, als ich vor einigen Jahren den Linux-Treibercode gelesen habe. Ich habe lange auf Baidu gesucht und wusste, wie man es benutzt, aber ich hatte immer noch nur ein vages Verständnis des Implementierungsprozesses und der Prinzipien. Das Makro container_of wird im Linux-Kernelcode sehr häufig verwendet. Für Studenten, die gerne Linux programmieren, ist das Verständnis seiner Implementierungsmethode eine große Hilfe beim Lesen von Kernelcode und beim Schreiben von Kerneltreibern in der Zukunft. Natürlich sage ich nicht, dass Sie nach dem Verständnis alles tun können, was Sie wollen. Der Kernel ist tiefgründig und umfangreich. Sie sollten zuerst vom Makro und dann vom Mikro lernen. Denken Sie nicht, dass Sie mit einem Bissen dick werden können. Dieser Artikel analysiert hauptsächlich das Implementierungsprinzip dieser Funktion und ich hoffe, er wird jedem in Ihrem Lernprozess hilfreich sein. android7.1/kernel/treiber/eingabe Kernel/Treiber/Eingabe$ grep -rn Container_von ./|wc -l 710 android7.1/kernel/drivers/input$ Verwenden Sie grep -rn container_of ./|wc -l, um zu zählen, wie oft container_of im Verzeichnis kernel/drivers/input/ vorkommt. Es gibt insgesamt 710 Verwendungen. 2. Die Rolle von container_of Die Funktion von container_of besteht darin, die Adresse der Struktur über die Adresse der Strukturmitgliedsvariable abzurufen. Angenommen, Ihr Name ist Li Guangming und Sie haben einen jüngeren Bruder namens XXX. Der Polizist fand heraus, dass Ihr Bruder XXX etwas Schlimmes getan hatte, aber der Polizist kannte den Namen Ihres Bruders nicht und verhaftete Sie deshalb zum Verhör. Sie waren jedoch sehr stur und weigerten sich, es ihm zu sagen. Der Polizist fand Ihren Namen heraus und überprüfte das Haushaltsregister Ihrer Familie. Dann wurde Ihr Bruder entdeckt. Es stellte sich heraus, dass Ihr Bruder XXX Li Xiaoming hieß. Diese Methode zum Lösen eines Falles nennt man „Hinweisen folgen“. Kernelfunktionsaufrufe übergeben häufig die Adresse eines Strukturelements, und dann möchte die Funktion andere Elementvariablen in der Struktur verwenden, was zu diesem Problem führt. Ich denke, dies ist auch eine Möglichkeit, objektorientierte Programmierung in C zu implementieren. Beispielsweise dieser Code statischer void sensor_suspend(Struktur early_suspend *h) { Struktur sensor_private_data *sensor = container_of(h, Struktur sensor_private_data, early_suspend); wenn (Sensor->Ops->Suspend) Sensor->Ops->Aussetzen(Sensor->Client); } early_suspend ist ein Mitglied von sensor_private_data. Die Adresse der Strukturvariablen sensor_private_data wird über die Adresse dieses Mitglieds erhalten, wodurch der darin enthaltene Mitgliedsvariable client aufgerufen wird. Diese Methode ist sehr elegant. Hier habe ich ein eleganteres Wort verwendet: „elegant“. Lassen Sie mich hier kurz erklären, dass das übergebene h irgendwo anders definiert worden sein muss und das Betriebssystem Speicherplatz dafür reserviert hat. Die Zuweisung von Speicherplatz für h bedeutet, dass sein Vater auch Speicher hat. Andernfalls wären Sie dumm, wenn Sie den Hinweisen folgen und ein NULL finden. 3. So verwenden Sie container_of Container_of muss drei Parameter übergeben. Der erste Parameter ist ein Zeiger, der zweite Parameter ist der Strukturtyp und der dritte Parameter ist das Mitglied in der Struktur, das dem zweiten Parameter entspricht.
4. Analyse der in container_of verwendeten Wissenspunkte 4.1. Die Rolle von ({}) ({}) Lassen Sie uns zunächst über diesen Ausdruck sprechen. Viele Leute verstehen ihn vielleicht und haben ihn vielleicht schon an vielen Stellen gesehen, aber sie achten möglicherweise nicht darauf. Dieser Ausdruck gibt den Wert des letzten Ausdrucks zurück. Wenn beispielsweise x=({a;b;c;d;}) ist, sollte der Endwert von x d sein. Codebeispiel: #include <stdio.h> Leere Haupt(Leere) { int a=({1;2;4;})+10; druckenf("%d\n",a);//a=14 } 4.2. typeof ermittelt den Variablentyp Das sehen wir selten. Dieses Schlüsselwort ist eine Erweiterung des Schlüsselworts der Sprache C und gibt den Typ der Variablen zurück. Weitere Einzelheiten finden Sie in der Einführung in GCC ++Eine andere Möglichkeit, auf den Typ eines Ausdrucks zu verweisen, ist mit typeof. Die Syntax für die Verwendung dieses Schlüsselworts ähnelt sizeof, aber die Konstruktion verhält sich semantisch wie ein mit typedef definierter Typname.++ Codebeispiel: Leere Haupt(Leere) { : Int a = 6; Typ von (a) b = 9; printf("%d %d\n",a,b); } 4.3. Die Rolle von (struct st*)0 Ich glaube, jeder hat schon einmal ein Lineal benutzt. Wenn ich beispielsweise mit einem Lineal die Länge eines Buches messen möchte, müssen wir zuerst die 0-Skalenposition des Lineals finden und dann diese 0-Skalenposition verwenden, um die Kante des Buches auszurichten, und es dann ausrichten. Überprüfen Sie die Linealskala auf der anderen Seite des Buches, um die Länge des Buches zu ermitteln. Jetzt müssen wir die Länge einer Struktur messen. Wir können dafür auch ein Lineal verwenden. Wir müssen nur die Position der 0-Skala finden. Auch wenn wir die Position der Skala 0 nicht kennen, können wir die Länge der Struktur berechnen, indem wir die erste und die letzte Skala subtrahieren. Aber was ist ein Lineal in C? Sie denken vielleicht an sizeof, aber das entspricht leider nicht unseren Anforderungen, daher haben wir (struct st *), was als Lineal wirklich perfekt ist. Struktur st{ int a; int b; }*p_st,n_st; Leere Haupt(Leere) { printf("%p\n",&((Struktur st*)0)->b); } Der obige Code (Struktur st*)0 Das heißt, wir legen diese Struktur auf die 0-Skala und beginnen mit der Messung. Wohin messen wir dann? &((Struktur st*)0)->b) Dies spiegelt sich bei der Messung bis zur Position b wider. Die Ausgabe oben sollte also 4 sein. Nachdem Sie die obige Erklärung gelesen haben, sollten Sie wissen, dass die folgenden beiden Codes dieselben Funktionen haben. typeof ((struct st*)0)->b) c; // Übernimm den Typ von b, um c zu deklarieren int c; Tatsächlich gilt dies nicht nur für 0, sondern auch für andere Zahlen. Im folgenden Code beispielsweise interessiert sich der Compiler für den Typ, nicht für die Zahl. printf("%p\n",&((Struktur st*)4)->b -4 ); Ich habe diesen Artikel vor ein paar Tagen geschrieben, wollte ihn aber nicht direkt veröffentlichen, weil ich das Gefühl hatte, nie einen besonders guten Weg gefunden zu haben, diesen Kernpunkt zu beweisen. Nachdem Sie das Obige gelesen haben, sollten Sie eine Vorstellung von dieser Art von Messung haben. Was sollten Sie tun, wenn Sie nun die erste Adresse eines Arrays auf 0 setzen müssen? Überlegen Sie einmal kurz, ob es zu einer Verzögerung von ein paar Minuten kommt. Der Code lautet wie folgt: Struktur A { kurzes Array[100]; }; int main(int argc, char *argv[]) { 1. Geben Sie die Zeichenfolge 10 ein. A*a = (A*)0; printf("%p %d %d\n",a,sizeof(kurz), &a->array[20]); getchar(); Rückgabe 1; } // Ausgabe 00000000 2 40 Gibt es eine Möglichkeit, die Adresse des Arrays direkt an Position 0 zu setzen, ohne ==struct A *== zu verwenden? Ich habe noch keine bessere Lösung gefunden. Wenn Sie gute Vorschläge haben, hinterlassen Sie mir bitte eine Nachricht. 4.4, Offset von (Typ, Mitglied) #define offsetof(TYP, MITGLIED) ((Größe_t) &((TYP*)0)->MITGLIED) Wenn Sie size_t nicht verstehen, können Sie auf Baidu danach suchen. Es handelt sich um eine vorzeichenlose Ganzzahl. Die Länge ist bei 32-Bit und 64-Bit unterschiedlich, daher wird offsetof verwendet, um die Offsetlänge der Struktur zu ermitteln. 4.5. Funktion von const int* p Es gibt auch einen kleinen Wissenspunkt in der obigen Makrodefinition const typeof( ((Typ *)0)->Mitglied ) *__mptr Der obige Code kann verkürzt werden zu const int * __mptr Was bedeutet das? Dies zeigt an, dass die von __mptr gezeigten Integer-Daten eine Konstante (Const) sind. Dabei handelt es sich um zwei weitere Erkenntnisse int * const __mptr; // bedeutet, dass der Wert von __mptr nicht geändert werden kann // und const int * const __mptr; // bedeutet, dass __mptr nicht geändert werden kann und der Inhalt, auf den es zeigt, nicht geändert werden kann 5. Analyse von container_of Nach dem Lesen der oben genannten Wissenspunkte wird das container_of-Makro sehr klar. Ich habe den Analyseteil in die Codekommentare unten eingefügt. #define offsetof(TYP, MITGLIED) ((Größe_t) &((TYP*)0)->MITGLIED) #define container_of(ptr, Typ, Mitglied) ({ \ const Typ von ((Typ *)0)->Mitglied) *__mptr = (const Typ von ((Typ *)0)->Mitglied) *)(ptr); \ (Typ *)( (Zeichen *)__mptr - Offset von (Typ, Mitglied) );}) //-----Trennlinie struct st{ int a; int b; }*pt; //Verwenden Sie dies als Beispiel container_of(&pt->a,struct st,a) const Typ von ( ((Struktur st *)0)->a ) *__mptr = (const Typ von ( ((Struktur st *)0)->a ) *)(&pt->a); const int *__mptr = (int *)(&pt->a);//Nach dem Parsen des ersten Satzes wird tatsächlich die Adresse von a abgerufen. (Typ *)( (Zeichen *)__mptr - Offset von (Typ, Mitglied) ); //Dies wird zu (struct st *)( (char *)__mptr - ((unsigned int) &((struct st*)0)->a)); //Dieser Satz bedeutet, dass die Adresse von a abzüglich der Offsetadresslänge von a zur Struktur die Adressposition der Struktur ist. 6. Beispielcode Nach der obigen Erklärung sollten Sie zumindest ein Gefühl für dieses Makro haben. Schreiben Sie einen Code, um es zu testen, und integrieren Sie sich in den Code. Nur so können Sie den Zustand der Einheit von Mensch und Code erreichen. Der Code lautet wie folgt: #include <stdio.h> #include<stddef.h> #include<stdlib.h> #define offsetof(TYP, MITGLIED) ((Größe_t) &((TYP*)0)->MITGLIED) /*ptr-Memberzeiger* Typstruktur wie struct Stu * Member-Member-Variable, die dem Zeiger entspricht* */ #define container_of(ptr, Typ, Mitglied) ({ \ const Typ von ((Typ *)0)->Mitglied) *__mptr = (const Typ von ((Typ *)0)->Mitglied) *)(ptr); \ (Typ *)( (Zeichen *)__mptr - Offset von (Typ, Mitglied) );}) Typdefinitionsstruktur Stu{ int Alter; Zeichenname[10]; Int-ID; vorzeichenlose lange Telefonnummer; }*p_stu,str_stu; void drucke_alles(void *p_str) { p_stu m1p_stu = NULL; m1p_stu = Container aus (p_str, Struktur Stu, Alter); printf("Alter:%d\n",m1p_stu->Alter); printf("Name:%s\n",m1p_stu->Name); printf("id:%d\n",m1p_stu->id); printf("Telefonnummer:%d\n",m1p_stu->Telefonnummer); } Leere Haupt(Leere) { p_stu m_stu = (p_stu)malloc(sizeof(str_stu)); m_stu->Alter = 25; m_stu->id = 1; m_stu->name[0]='w'; m_stu->name[1]='e'; m_stu->name[2]='i'; m_stu->name[3]='q'; m_stu->name[4]='i'; m_stu->name[5]='f'; m_stu->name[6]='ein'; m_stu->name[7]='\0'; m_stu->Telefonnummer=13267; /* Übergeben Sie den Zeiger des Strukturmitglieds in */ drucken_alles(&m_stu->age); printf("Hauptende\n"); if(m_stu!=NULL) frei(m_stu); } 7. Programmausgabe
Zusammenfassen Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an Das könnte Sie auch interessieren:
|
<<: Analyse des Hintergrundauthentifizierungsprozesses von Vue-Elementen
Schritt 1: yum install httpd -y #httpd-Dienst ins...
Dieser Artikel beschreibt, wie man die PHP-Curl-E...
Ich sehe viele Anfänger in der Front-End-Entwicklu...
Inhaltsverzeichnis Datenspalten isolieren Präfixi...
Daten in MySQL-Datenbank einfügen. Bisher häufig ...
Inhaltsverzeichnis 1 Bewertung 2 Fünf Strategien ...
So löschen Sie den in Docker erstellten Container...
Nach dem Docker-Lauf ist der Status immer „Beende...
Was soll ich tun, wenn Linux nicht alle Befehle u...
Über JS, CSS CSS: Stylesheet oben Vermeiden Sie C...
[Verwendung und Funktion des MySQL-Cursors] Beisp...
Startups überraschen uns oft mit ihren unkonventi...
Um den Inhaltstyp zu lernen, müssen Sie zunächst ...
Inhaltsverzeichnis 1 Was ist Funktions-Currying? ...
So erstellen Sie eine virtuelle Linux-Maschine in...