Detaillierte Erklärung des Linux-Kernel-Makros Container_Of

Detaillierte Erklärung des Linux-Kernel-Makros Container_Of

1. Wie werden Strukturen im Gedächtnis gespeichert?

int main() 
{ 
 
 Student(in) 
 stu.id = 123456; 
 strcpy(stu.name,"feizhufeifei"); 
 stu.math = 90; 
 stu.PE = 80; 
 printf("Student:%p\r\n",&stu); 
 printf("stu.ID:%p\r\n",&stu.ID); 
 printf("stu.name:%p\r\n",&stu.name); 
 printf("stu.math:%p\r\n",&stu.math); 
 gebe 0 zurück; 
} 

Die Druckergebnisse sind wie folgt:

//Die Adresse der Struktur Student:0xffffcbb0 
//Die Adresse des ersten Mitglieds der Struktur stu.ID:0xffffcbb0 //Offsetadresse + 0 
stu.name:0xffffcbb4//Offset-Adresse + 4 
stu.math:0xffffcbd4 //Offset-Adresse + 24 


Wir können sehen, dass die Adresse der Struktur mit der Adresse des ersten Mitglieds der Struktur übereinstimmt. Aus diesem Grund haben wir in „Weigern Sie sich, das Rad neu zu erfinden! So portieren und verwenden Sie die allgemeine verknüpfte Liste des Linux-Kernels (mit vollständiger Codeimplementierung)“ erwähnt, warum die Struktur „list_head“ an erster Stelle in der Struktur stehen sollte.

Wenn Sie es nicht ganz verstehen, schauen Sie sich diese beiden Beispiele an:

  • struct A { int a; char b; int c; char d; }; a befindet sich bei Offset 0, b bei Offset 4, c bei Offset 8 (das kleinste ganzzahlige Vielfache von 4, das größer als 4 + 1 ist) und d bei Offset 12. A hat Ausrichtung 4 und Größe 16.
  • struct B { int a; char b; char c; long d; }; a befindet sich bei Offset 0, b bei Offset 4, c bei Offset 5 und d bei Offset 8. B hat eine Ausrichtung von 8 und eine Größe von 16.

Wir können sehen, dass die Membervariablen in der Struktur tatsächlich als Offset-Adressen im Speicher gespeichert sind. Das heißt, die Adresse der Struktur A + die Offset-Adresse der Mitgliedsvariable = die Startadresse der Strukturmitgliedsvariable.

Daher können wir anhand der Startadresse der Strukturvariablen und der Offsetadresse der Membervariablen auch auf die Adresse der Struktur A schließen.

2. container_of-Makro

#define offsetof(TYP, MITGLIED) ((Größe_t) &((TYP*)0)->MITGLIED) 
#define container_of(ptr, Typ, Mitglied) ({ \ 
        const Typ von (((Typ *)0)->Mitglied)*__mptr = (ptr); \ 
    (Typ *)((char *)__mptr - Offset von (Typ, Mitglied)); }) 


??Schauen wir uns zunächst die drei Parameter an. ptr ist der Zeiger auf die Membervariable, type bezieht sich auf den Typ der Struktur und member ist der Name der Membervariable.

Die Funktion des Makros „container_of“ besteht darin, die Adresse einer Strukturvariablen über die Adresse einer Mitgliedsvariablen in der Struktur, den Variablennamen und den Strukturtyp zu finden. Ein hier verwendeter Trick besteht darin, die Compilertechnologie zu verwenden, bei der zuerst der Offset des Strukturmitglieds in der Struktur ermittelt wird und dann basierend auf der Adresse der Mitgliedsvariablen die Adresse der Hauptstrukturvariablen ermittelt wird. Es folgt eine detaillierte Analyse jedes Teils.

3. Art von

Schauen wir uns zunächst typeof an, das verwendet wird, um den Typ einer Variablen zurückzugeben. Dies ist eine erweiterte Funktion des GCC-Compilers, was bedeutet, dass typeof vom Compiler abhängig ist. Es wird weder von der C-Sprachspezifikation gefordert, noch ist es Teil eines Standards.

int main() 
{ 
 : Int a = 5; 
 //Definieren Sie hier eine Variable b vom gleichen Typ wie a 
 Typ von (a) b = 6; 
 printf("%d,%d\r\n",a,b);//5 6 
 gebe 0 zurück; 
} 

4. (((Typ *)0)->Mitglied)

((TYPE *)0) konvertiert 0 in einen Strukturzeiger vom Typ type. Mit anderen Worten, der Compiler denkt, die Struktur beginne am Anfang des Programmsegments, also bei 0. Wenn sie bei Adresse 0 beginnt, ist die Adresse der Membervariablen, die wir erhalten, direkt gleich der Offsetadresse der Membervariablen.

(((Typ *)0)->Mitglied) bezieht sich auf das MEMBER-Mitglied in der Struktur.

Typdefinitionsstruktur Student{ 
 Int-ID; 
 Zeichenname[30]; 
 int-Mathematik; 
}Student; 
int main() 
{ 
 //Hier wird die Struktur zwangsweise in die Adresse 0 konvertiert und dann die Adresse des Namens gedruckt. 
 printf("%d\r\n",&((Student *)0)->Name);//4 
 gebe 0 zurück; 
} 

5. const typeof(((Typ * )0) ->Mitglied)*__mptr = (ptr);

Dieser Code bedeutet, dass mit typeof() der Typ des member in der Struktur ermittelt wird, dann eine temporäre Zeigervariable __mptr dieses Typs definiert wird und __mptr die Adresse des member zugewiesen wird, auf das ptr zeigt.

Warum verwenden Sie nicht einfach ptr anstatt dies zu tun? Ich denke, es könnte dazu dienen, eine Beschädigung der von ptr und prt referenzierten Inhalte zu vermeiden.

6. offsetof(Typ, Mitglied))

((Größe_t) &((TYP*)0)->MITGLIED)

size_t ist in der Standard-C-Bibliothek definiert und wird auf 32-Bit-Architekturen im Allgemeinen wie folgt definiert:

Typdefinition: vorzeichenlose Int. Größe_t;

Auf 64-Bit-Architekturen wird es wie folgt definiert:

Typdefinition: vorzeichenlos lang, Größe_t;

Wie Sie der Definition entnehmen können, ist size_t eine nicht negative Zahl. Daher wird size_t normalerweise zum Zählen verwendet (da zum Zählen kein negativer Bereich erforderlich ist):

für (size_t i=0;i<300;i++)

Um das Programm portabler zu machen, verwendet der Kernel size_t anstelle von int und unsigned。((size_t) &((TYPE*)0)->MEMBER) In Kombination mit der vorherigen Erklärung können wir erkennen, dass dieser Satz bedeutet, den Offset-Wert von MEMBER relativ zur Adresse 0 zu finden.

7. (Typ *)((Zeichen *)__mptr - Offset von (Typ, Mitglied))

Dieser Satz bedeutet, __mptr in char* 類型zu konvertieren. Weil der durch offsetof erhaltene Offset in Bytes angegeben ist. Subtrahieren Sie die beiden, um die Startposition der Struktur zu erhalten, und erzwingen Sie dann die Konvertierung in type .

8. Beispiele

#define offsetof(TYP, MITGLIED) ((size_t) &((TYP *)0)->MITGLIED) 
#define container_of(ptr, Typ, Mitglied) ({ \ 
        const typeof( ((Typ *)0)->Mitglied ) *__mptr = (ptr); \ 
        (Typ *)( (Zeichen *)__mptr - Offset von (Typ, Mitglied) );}) 
         
Typdefinitionsstruktur Student 
{ 
 Int-ID; 
 Zeichenname[30]; 
 int mathe; 
}Student; 
 
int main() 
{ 
    Student(in) 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
        sptr = container_of(&stu.id,Student,id); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.name,Student,name); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.math,Student,id); 
        printf("sptr=%p\n",sptr); 
        gebe 0 zurück;  
} 


Die Ergebnisse sind wie folgt:

sptr=0xffffcb90
sptr=0xffffcb90
sptr=0xffffcbb4

Makro-Erweiterung kann es klarer machen

int main() 
{ 
    Student(in) 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
  //Erweitern und ersetzen Sie sptr = ({ const unsigned char *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );}); 
        printf("sptr=%p\n",sptr); 
        //Erweitern und ersetzen Sie sptr = ({ const unsigned char *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );}); 
        printf("sptr=%p\n",sptr); 
        //Erweitern und ersetzen Sie sptr = ({ const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );}); 
        printf("sptr=%p\n",sptr); 
        gebe 0 zurück;  
} 

Dies ist das Ende dieses Artikels mit der detaillierten Erklärung des Container_Of-Makros im Linux-Kernel. Weitere Informationen zum Container_Of-Makro im Linux-Kernel finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Detaillierte Erläuterung des Funktionsfalls des C-Sprachcontainers of()
  • Einführung in die Containerfunktion of() in der Linux-Kernel-Programmierung
  • Detaillierte Analyse des Linux-Kernel-Makros container_of
  • Detaillierte Erklärung der container_of-Funktion im Linux-Kernel
  • Einführung in den Makrofunktionscontainer von () in der C-Sprache

<<:  Praktisches MySQL + PostgreSQL Batch-Insert-Update insertOrUpdate

>>:  Einführung in die Verwendung von this in HTML-Tags

Artikel empfehlen

Lösung für die Ausnahmen 1449 und 1045 bei der Verbindung mit MySQL

Lösung für die Ausnahmen 1449 und 1045 bei der Ve...

Saubere XHTML-Syntax

Das Schreiben von XHTML erfordert eine saubere HTM...

Importieren Sie die CSV-Datei mit Navicat in MySQL

In diesem Artikel wird der spezifische Code zum I...

Schritte eines hervorragenden Registrierungsprozesses

Für eine Website ist dies die grundlegendste Funkt...

Auszeichnungssprache - Stylesheets drucken

Klicken Sie hier, um zum Abschnitt „HTML-Tutorial“...

Detaillierte Analyse der Kompilierung und Installation von vsFTP 3.0.3

Details zur Sicherheitsanfälligkeit VSFTP ist ein...

Zwei Arten von Tab-Anwendungen im Webdesign

Heutzutage werden Registerkarten häufig im Webdes...

So ändern Sie das Root-Passwort in einem Container mit Docker

1. Verwenden Sie den folgenden Befehl, um das SSH...

Analyse des Unterschieds zwischen relativem und absolutem HTML-Pfad

Gerade HTML-Anfänger stehen häufig vor dem Problem...

Detaillierte Erklärung des Rewrite-Moduls von Nginx

Das Umschreibmodul ist das Modul ngx_http_rewrite...

So lösen Sie das domänenübergreifende Front-End-Problem mithilfe des Nginx-Proxys

Vorwort Nginx (ausgesprochen „Engine X“) ist ein ...