Detaillierte Erläuterung des Makros container_of im Linux-Kernel

Der letzte Abschnitt weigerte sich, Räder herzustellen! Transplantation und Verwendung der allgemeinen verknüpften Liste des Linux-Kernels (mit vollständiger Code-Implementierung) Bei der Analyse der verknüpften Liste des Linux-Kernels haben wir festgestellt, dass der Kernel beim Lösen des Strukturversatzes die Makrodefinition container_of geschickt verwendet hat. Heute werden wir den Kernel im Detail analysieren. So lösen Sie die Adresse der Strukturelementvariablen.

Wie die Struktur gespeichert wird

int main()
{
    
    

	Student stu;
	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);
	return 0;
}

  Das Druckergebnis lautet wie folgt:

//结构体的地址
Student:0xffffcbb0
//结构体第一个成员的地址
stu.ID:0xffffcbb0  //偏移地址 +0
stu.name:0xffffcbb4//偏移地址 +4
stu.math:0xffffcbd4//偏移地址 +24

  Wir können sehen, dass die Adresse der Struktur mit der Adresse des ersten Mitglieds der Struktur übereinstimmt . Deshalb haben wir uns vorher geweigert, Räder herzustellen! Wie man einen Linux-Kernel transplantiert und eine generische Liste verwendet (beigefügte vollständige Code-Implementierung) Warum arbeiten wir in der Struktur, struct list_headdie an erster Stelle erwähnt wurde ?

Wenn Sie nicht verstehen, schauen wir uns diese beiden Beispiele an.
Struktur A {int a; char b; int c; char d;}; ein Versatz ist 0, b Versatz ist 4, c Versatz ist 8 (größer als 4 + 1) Das kleinste ganzzahlige Vielfache von 4), der Versatz von d ist 12. A ist auf 4 ausgerichtet und die Größe ist 16.
Struktur B {int a; char b; char c; long d;}; ein Versatz ist 0, b Versatz ist 4, c Versatz ist 5, d Versatz ist 8. B ist auf 8 ausgerichtet und die Größe ist 16.

Fügen Sie hier eine Bildbeschreibung ein

  Wir können sehen, dass die Mitgliedsvariablen in der Struktur tatsächlich versetzte Adressen sind, die im Speicher gespeichert sind . Das heißt , die Adresse der Struktur A + die Versatzadresse der Elementvariablen = die Startadresse der Strukturelementvariablen . Daher können wir die Adresse der Struktur A auch basierend auf der Startadresse der Strukturvariablen und der Versatzadresse der Elementvariablen ableiten.

container_of Makro

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)
#define container_of(ptr, type, member) ({          \
        const typeof(((type *)0)->member)*__mptr = (ptr);    \
    (type *)((char *)__mptr - offsetof(type, member)); })

  Schauen Sie sich zunächst die drei Parameter an: ptr ist ein Zeiger auf eine Elementvariable, type ist der Strukturtyp und member ist der Name einer Elementvariablen.

  Die Funktion des Makros container_of besteht darin, die Adresse einer Mitgliedsvariablen in der Struktur , den Variablennamen und den Strukturtyp zu übergeben . Suchen Sie die Adresse der Strukturvariablen. Was gebraucht wird , ist hier eine kleine Trick mit Compiler - Technologie, das heißt, erhält zuerst die Offset von dem Strukturelement in der Struktur , und dann erhalten Sie die Adresse des Hauptstrukturvariablen nach der Adresse der Membervariable . Die folgende spezifische Analyse jedes Teils:

eine Art von

  Schauen Sie sich zunächst typeof an, mit dem der Typ einer Variablen zurückgegeben wird . Dies ist eine erweiterte Funktion des GCC-Compilers. Dies bedeutet, dass typeof mit dem Compiler zusammenhängt. Es ist weder in der C-Sprachspezifikation noch in einem Standard vorgeschrieben.

eine Art von

int main()
{
    
    
	int a = 5;
	//这里定义一个和a类型相同的变量b
	typeof(a) b  = 6;
	printf("%d,%d\r\n",a,b);//5 6
	return 0;
}

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

  ((TYPE *)0)Konvertieren Sie 0 in einen Strukturzeiger vom Typ Typ. Mit anderen Worten, lassen Sie den Compiler denken, dass diese Struktur am Anfang des Programmsegments bei 0 beginnt. Wenn sie bei Adresse 0 beginnt, ist die Adresse der Mitgliedsvariablen, die wir erhalten, direkt gleich der Mitgliedsvariablen. Die Offset-Adresse ist out.
   (((type *)0)->member)Beziehen Sie sich auf das Mitglied MITGLIED in der Struktur.

typedef struct student{
    
    
	int id;
	char name[30];
	int math;
}Student;
int main()
{
    
    
	//这里时把结构体强制转换成0地址,然后打印name的地址。
	printf("%d\r\n",&((Student *)0)->name);//4
	return 0;
}

const typeof (((type ) 0) -> member) __mptr = (ptr);

   Dieser Code bedeutet, typeof () zu verwenden, um den Typ des Mitgliedsattributs in der Struktur abzurufen , dann eine temporäre Zeigervariable __mptr dieses Typs zu definieren und die Adresse des Mitglieds, auf das ptr zeigt, __mptr zuzuweisen.
  Warum nicht ptr direkt verwenden? Aber was ist damit? Ich denke, es könnte sein, den Inhalt, auf den ptr und prt zeigen, nicht zu beschädigen.

offsetof (Typ, Element))

((size_t) &((TYPE*)0)->MEMBER)

   size_tEs ist in der Standard-C-Bibliothek definiert und wird in der 32-Bit-Architektur im Allgemeinen wie folgt definiert:

typedef unsigned int size_t;

  Und in der 64-Bit-Architektur ist definiert als:

typedef unsigned long size_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 für das Zählen kein negativer Bereich erforderlich ist):

for(size_t i=0;i<300;i++)

  Um eine gute Portabilität des Programms zu gewährleisten, verwendet der Kernel size_t und anstelle von int und unsigned.
((size_t) &((TYPE*)0)->MEMBER)In Kombination mit der vorherigen Erklärung können wir wissen, dass die Bedeutung dieses Satzes darin besteht, MEMBEReinen Versatzwert relativ zur 0-Adresse zu finden.

(Typ *) ((char *) __ mptr - offsetof (Typ, Mitglied))

   Die Bedeutung dieses Satzes besteht darin, __mptr in den Typ char * umzuwandeln, da der durch offsetof erhaltene Offset in Bytes angegeben ist. Subtrahieren Sie die beiden, um die Startposition der Struktur zu erhalten, und zwingen Sie sie dann zur Eingabe.

Beispielsweise

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
        (type *)( (char *)__mptr - offsetof(type,member) );})
        
typedef struct student
{
    
    
	int id;
	char name[30];
	int math;
}Student;

int main()
{
    
    
  		Student stu;
        Student *sptr = NULL;
		stu.id = 123456;
		strcpy(stu.name,"feizhufeifei");
		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);
        return 0;	
}

  Die Ergebnisse sind wie folgt:

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

  Die Makroerweiterung kann es klarer machen

int main()
{
    
    
  		Student stu;
        Student *sptr = NULL;
		stu.id = 123456;
		strcpy(stu.name,"feizhufeifei");
		stu.math = 90;
		//展开替换
        sptr = ({
    
     const unsigned char  *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );});
        printf("sptr=%p\n",sptr);
        //展开替换
        sptr = ({
    
     const unsigned char  *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );});
        printf("sptr=%p\n",sptr);
        //展开替换
        sptr = ({
    
     const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );});
        printf("sptr=%p\n",sptr);
        return 0;	
}

  Entwickeln Sie eine Gewohnheit, wie zuerst und dann beobachten! Wenn Sie der Meinung sind, dass das Schreiben gut ist, können Sie gerne folgen, wie, Favorit, vorwärts, danke!
  Der obige Code ist der Code nach dem Test. Wenn es Fehler und Unzulänglichkeiten gibt, weisen Sie bitte darauf hin.

Ich denke du magst

Origin blog.csdn.net/qq_16933601/article/details/108576447
Empfohlen
Rangfolge