Les listes chaînées dans Linux 2.6

Devenez un artiste en créant des listes doublement chaînées.
Découvrez la macro containerof basée sur la fonction offsetof.

Structure des chaînes



A la différence de ce que l'on a appris généralement en cours ici les listes ne pointent pas sur la structure mais sur le chaînon suivant. Avantages ?
Mais alors, pour manipuler un champ de la structure, il faudra qu'en même être capable de récuperer l'adresse de la structure par l'intermédiaire du chaînon.

La difficulté majeur est la compréhension de containerof. Mais revenons tout d'abord à la macro offsetof à la base de la macro containeurof.

La macro offsetof

Voici un exemple de structure avec l'adresse des différents champ est l'offset en octet avec le début de la structure. Nous allons voir comment calculer facilement l'écart en octet entre le début d'une structure et un champ grace à la macro offsetof.
offset
remarque : Pour éviter ces décalages on  utilise __attribute__ ((packed)) dans la définition de la structure.

Définition de offsetof

offsetof(struct S,champ))= &((struct S *)0)->champ

La définition n'est pas si évidente, mais retenons l'idée sous jacente.

Pour savoir l'écart entre un champ et le début de la structure, on met la structure à l'adresse 0. Ainsi, l'adresse du champ donne l'écart entre le début de la structure et l'adresse du champ. L'adresse de début de la structure valant zéro, on l'oublie par la suite dans le calcul de l'adresse du champ.


Dans l'exemple précédent,
offsetof(struct humain,age)= -(struct humain *)0 + &((struct nomprenomage *)0)->age
offsetof(struct humain,age)= -0+8 = 8 = &((struct nomprenomage *)0)->age
On comprend alors la définition.

Containerof

Containerof permet de renvoyer l’adresse du début de la structure connaissant l’adresse d’un de ces membres. Par exemple ptr = container_of(ptrhumain ->age,struct humain,age);
ptr = 0xff2.

Idée

On a remarqué que les chaînons ne pointent pas sur la structure que l'on veut manipuler mais sur un membre (qui est d'ailleurs le chaînon suivant). Grâce à containerof, on accède à la structure par le chaînon et containerof va renvoyer l'adresse de la structure. Cette adresse permet ensuite de manipuler tous les champs de la structure.

définition

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

La définition est en deux parties :
une déclaration d'un pointeur __mptr  de type const typeof( ((type *)0)->member )* et une affectation
le calcul d'une adresse : (char *)__mptr - offsetof(type,member)

La déclaration "dit" que __mptr est un pointeur (*)  de type membre (member) d'une structure de nom type que l'on force à l'adresse 0. Puis le pointeur __mptr est égal à l'adresse de ptr.
le calcul est une soustraction entre la valeur du pointeur (sur un membre connu) et le décalage du membre avec le début de la structure. Cette valeur renvoie donc l'adresse du début de la structure.

Dans l'exemple ci-dessus : on aurrait (char *)__mptr=ffa et offsetof(type,member)=8, le résultat de la soustraction donne l'adresse du pointeur sur la structure.
 
Retour